Published
Edited
Jan 29, 2021
Importers
Insert cell
Insert cell
Insert cell
Insert cell
colours = {
return {
primary: '#C1272D',
primary50: '#FF8186',
primary00: '#FFD1D3',
secondary: '#000000',
secondary50: '#575757',
secondary00: '#BEBEBE',
chart: [
'#000000',
'#434343',
'#C1272D',
'#C4C4C4'
]
};
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
async function labelledSeries (arr) {
let csvs = arr.map(async ([label,url]) => {
// submit an array of [["Label"], [CSVURLSTRING]]
return {
label: label,
data: await d3.csv(url, d3.autoType)
}
})
let table = await Promise.all(csvs)
table.labels = arr.map(([label,url])=>label)
return table;
}
Insert cell
Insert cell
// Taser incidents in a year, optionally by force, optionally per 100 officers
taserUse = (force='Total',
year=cedCounts.labels[cedCounts.labels.length - 1],
per100 = false) => {
const data = cedCounts.find(d => d.label == year).data
let cols = [...typesOfCedUse,"Total"]

if (force=="Total") {
let total = sumUsage(data)
let pop = officerFteSearch(year)
return Object.fromEntries(cols.map(type => [type, total[type]/(per100 ? pop/100 : 1.0)]))
}
else {
// select use of ced data for force
let use = data.find(i=>i.force == force)
// select population by force
let pop = officerFteCounts.find(el=>el.label==year).data.find(el=>el.force==force)
let dividend = (per100 ? pop.fte/100 : 1.0)
return Object.fromEntries(cols.map(type=>{
return [type,use[type]/dividend]
}))
}
}
Insert cell
taserUse("Northamptonshire",2019,true)
Insert cell
Insert cell
sumUsage = (yeardata)=>{
return yeardata.reduce((acc,force)=>{
for(let i in typesOfCedUse) {
acc[typesOfCedUse[i]] += force[typesOfCedUse[i]]
}
acc.Total = typesOfCedUse.reduce((i,val)=>i+acc[val],0)
return acc
},Object.fromEntries(typesOfCedUse.map(k=>[k,0])))
}
Insert cell
Insert cell
Insert cell
officerFteCounts = {
var URLPrefix = OFFICER_FTE_SRCDIR
const officerCountsByYear = await labelledSeries([
["2015",URLPrefix+"2015.csv"],
["2016",URLPrefix+"2016.csv"],
["2017",URLPrefix+"2017.csv"],
["2018",URLPrefix+"2018.csv"],
["2019",URLPrefix+"2019.csv"]])
let countsByYearByForceOnly = officerCountsByYear.map(({label:l,data:d})=>{
return {
label:l,
data:d
.filter(el => forces.indexOf(el['Force/Region']) > -1)
.map(el => {
return {force: el['Force/Region'], fte: el["March "+l]}
})
}
})
countsByYearByForceOnly.labels = officerCountsByYear.labels
return countsByYearByForceOnly
}
Insert cell
officerFteSearch = (force = false, year) => {
if(force) {
let ydata = officerFteCounts.find(d => d.label == year)
let fdata = ydata.data.find(f => f.force == force)
return fdata.fte
} else {
let ydata = officerFteCounts.find(d => d.label == year).data
return ydata.reduce((acc,val)=>acc+ val.fte,0)
}
}
Insert cell
officerFteSearch("Greater Manchester", 2019)
Insert cell
cedPer100Search = (force, year) => {
let forcePop = officerFteCounts.find(e=>e.label==year).data.find(e=>e.force==force).fte;
let forceStats = cedCounts.find(slice=>slice.label==year).data.find(f=>f.force==force);
return forceStats.Total * 100 / forcePop;
}
Insert cell
cedPer100Search("Greater Manchester", 2019)
Insert cell
Insert cell
cedCounts = {
const data = await labelledSeries(
[["2016", CED_2016_SRC],
["2017-first-quarter", CED_2017_Q1_SRC],
["2018", CED_2017_18_SRC],
["2019", CED_2018_19_SRC],
["2020", CED_2019_20_SRC]])

let filt = data.map(({label:l,data:d})=>{
return {
label: l,
data: d.filter(el=>forces.indexOf(el['Police force']) > -1)
.map(el=>{
let r = {}
r.force = el['Police force']
for(let i in typesOfCedUse) {
r[typesOfCedUse[i]] = el[typesOfCedUse[i]]
}
r.Total = typesOfCedUse.reduce((acc,val)=>acc+r[val],0)
return r
})
}
})
filt.labels = data.labels
return filt
}
Insert cell
Insert cell
yoyIncPc = (force,year1, year2)=>{
return taserUse(force, year2).Total/taserUse(force, year1).Total * 100 - 100
}
Insert cell
yoyIncPc("Greater Manchester", 2018, 2019)
Insert cell
officerCounts2019 = d3
.csv(OFFICER_HEADCOUNT_2019_SRC)
.then(data => {
data.forEach(d => {
// typecasting from string to int for the data
d[data.columns[1]] = +d[data.columns[1]];
d[data.columns[2]] = +d[data.columns[2]];
d[data.columns[3]] = +d[data.columns[3]];
d[data.columns[4]] = +d[data.columns[4]];
});
return data;
})
Insert cell
ced_by_region_2018_19 = d3
.csv(CED_2018_19_SRC)
.then(data => {
data.forEach(d => {
// typecasting from string to int for the data
d[data.columns[2]] = +d[data.columns[2]];
d[data.columns[3]] = +d[data.columns[3]];
d[data.columns[4]] = +d[data.columns[4]];
d[data.columns[5]] = +d[data.columns[5]];
d[data.columns[6]] = +d[data.columns[6]];
d[data.columns[7]] = +d[data.columns[7]];
d[data.columns[8]] = +d[data.columns[8]];
d[data.columns[9]] = +d[data.columns[9]];
d[data.columns[10]] = +d[data.columns[10]];
d[data.columns[11]] = +d[data.columns[11]];
d[data.columns[12]] = +d[data.columns[12]];
});
return data;
})
Insert cell
ced_by_force_2018_19 = ced_by_region_2018_19.filter(i=>i["Police force"][0] != "*")
Insert cell
allCedUse = {
// For new use of force stat comparison
let URLPrefix = CED_SRCDIR
let cedTotalsRaw = await labelledSeries([
["2019", URLPrefix+"april2018-march2019.csv"],
["2020", URLPrefix+"april2019-march2020.csv"]
])
let cedTotalsCleaned = cedTotalsRaw.map(d => {
return d.data.filter((row) => row['Police force'][0] != '*')
})
return cedTotalsCleaned
}
Insert cell
Insert cell
pre2011 = d3.csv(CED_PRE2011_SRC,d3.autoType)
Insert cell
total0910 = pre2011.reduce((acc,force)=>{
let keys=["April-June 2009 Totals","July-September 2009 Totals","October-December 2009 Totals","January-March 2010 Totals"]
keys.map(k=>{acc += force[k]})
return acc
},0)
Insert cell
// borrowed from https://github.com/d3/d3-time-format/issues/10
function ordinalSuffix(number) {
const ordinalSuffixes = ['th', 'st', 'nd', 'rd'];

const value = number % 100;

return `${number}${ordinalSuffixes[(value - 20) % 10] || ordinalSuffixes[value] || ordinalSuffixes[0]}`;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@6")
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