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

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