Public
Edited
Mar 26
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
years = [...new Set(derivedData.map((d) => d.year).reverse())]
Insert cell
// x encodes the category class.
x = d3
.scaleBand()
.domain(years)
.rangeRound([
chartDimensions.margin.left,
chartDimensions.width - chartDimensions.margin.right
])
.padding(0.1)
Insert cell
yDomain = {
const maxYValue = Math.max(
0,
Math.ceil(d3.max(series, (d) => d3.max(d, (d) => d[1])))
);
const minYValue = Math.min(
0,
Math.floor(d3.min(series, (d) => d3.min(d, (d) => d[0])))
);

return [minYValue, maxYValue];
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colors = ({
Delikte: {
"Im ausserhäuslichen Umfeld": ZHColors.chart.blue,
"Im häuslichen Umfeld": ZHColors.chart.darkblue
}
})
Insert cell
symbols = ({
Delikte: {
"Im ausserhäuslichen Umfeld": d3.symbolSquare,
"Im häuslichen Umfeld": d3.symbolSquare
}
})
Insert cell
localeDeCH = {
const res = await fetch(
"https://cdn.jsdelivr.net/npm/d3-format@1/locale/de-CH.json"
);
const data = await res.json();
return d3.formatLocale(data);
}
Insert cell
formatValue = (v) =>
isNaN(v) ? "keine Angaben" : `${localeDeCH.format(",.0f")(v)}`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
htmlSectorSymbol = (name) =>
lit.html`<svg style="display: inline-block; aspect-ratio: 1; width: 1em; transform: translateY(2px) scale(1.5); margin-right: .25rem" viewBox="0 0 1 1">${lit.svg`<path transform="translate(0.5 0.5)"
fill="${getSectorColor(name)}" d="${d3
.symbol()
.size(0.25)
.type(getSectorSymbol(name))()}" />`}</svg>`
Insert cell
getSectorSymbol = (name) => {
return symbols.Delikte[name];
}
Insert cell
getSectorColor = (name) => {
return colors.Delikte[name];
}
Insert cell
Insert cell
// Defined sort order based on the order of the drive types
sortByObject = category.reduce((obj, item, index) => {
return {
...obj,
[item]: index
};
}, {})
Insert cell
Insert cell
fetchedData = await d3
.csv(dataPath)
// await FileAttachment("KTZH_00001202_00005085_2025.csv")
// .csv()
.then(function (data) {
data.forEach(function (d) {
(d.year = new Date(d.Ausgangsjahr)),
(d.Anzahl =
d.Anzahl === "NA" ? Number.parseInt(0) : Number.parseInt(d.Anzahl)),
(d.Gewaltkategorie =
d.Gewaltkategorie === "HG"
? "Im häuslichen Umfeld"
: "Im ausserhäuslichen Umfeld");
});
return data;
})
Insert cell
derivedData = tidy(
fetchedData,
// filter((d) => d.Gewaltkategorie == "Im häuslichen Umfeld"),
groupBy(["year", "Gewaltkategorie"], summarize({ total: sum("Anzahl") }))
)
Insert cell
/*
* Group by Sektor and rename properties
*/
groupedByCategory = derivedData
.sort(
(a, b) => sortByObject[a.Gewaltkategorie] - sortByObject[b.Gewaltkategorie]
)
.reduce((groups, d) => {
const group = groups.find((g) => g.name === d.Gewaltkategorie);

const currentValue = {
value: d.total,
year: d.year,
sector: d.Gewaltkategorie
};

if (group) {
group.values.push(currentValue);
group.valuesByYear[currentValue.year.getFullYear()] = currentValue.value;
} else {
groups.push({
name: d.Gewaltkategorie,
values: [currentValue],
valuesByYear: {
[currentValue.year.getFullYear()]: currentValue.value
}
});
}

return groups;
}, [])
.map((group) => ({
...group,
values: group.values //.sort((d) => d3.descending(d.sector))
}))
Insert cell
series = {
const data = derivedData.sort(
(a, b) => sortByObject[a.Gewaltkategorie] - sortByObject[b.Gewaltkategorie]
);
return d3
.stack()
.keys(d3.union(derivedData.map((d) => d.Gewaltkategorie))) // distinct seriesHG keys, in input order
.value(([, D], key) => D.get(key).total)(
// get value for each seriesHG key and stack
d3.index(
derivedData,
(d) => d.year,
(d) => d.Gewaltkategorie
)
);
}
Insert cell
category = {
const gewaltkategorien = [
...new Set(derivedData.map((d) => d.Gewaltkategorie))
];

return gewaltkategorien.reverse();
}
Insert cell
Insert cell
Insert cell
titel = "Opfer von Gewaltdelikten"
Insert cell
subtitel = `Zwischen ${yearExtent.join(" und ")}`
Insert cell
unit = "Anzahl Opfer"
Insert cell
source = "Kantonspolizei Zürich, Polizeiliche Kriminalstatistik (PKS)"
Insert cell
Insert cell
import {
colors as ZHColors,
fonts as ZHFonts
} from "@statistikzh/common-styles"
Insert cell
lit = import(`https://esm.run/lit-html@3`) // import("lit-html@3.2.1")
Insert cell
Insert cell
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