diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2019-08-05 02:06:29 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2019-08-05 02:06:29 +0200 |
| commit | 626c348af622b9bb66000d1a49dbe007131649ef (patch) | |
| tree | 59127e1e69a209c1f0823050c17979cd5253d312 /bin | |
sonifications
Diffstat (limited to 'bin')
| -rw-r--r-- | bin/grep.js | 143 | ||||
| -rw-r--r-- | bin/group-mass.js | 137 | ||||
| -rw-r--r-- | bin/group.js | 263 | ||||
| -rw-r--r-- | bin/media.js | 117 |
4 files changed, 660 insertions, 0 deletions
diff --git a/bin/grep.js b/bin/grep.js new file mode 100644 index 0000000..67e5e4a --- /dev/null +++ b/bin/grep.js @@ -0,0 +1,143 @@ + +/* + This script parses the big gun violence CSV and filters for lines containing a particular string. + + 479363, + 2013-01-19,New Mexico,Albuquerque,2806 Long Lane, + 5,0, + http://www.gunviolencearchive.org/incident/479363, + http://hinterlandgazette.com/2013/01/pastor-greg-griego-identified-victims-killed-nehemiah-griego-albuquerque-nm-shooting.html, + False,1, + + gun_stolen, + 0::Unknown||1::Unknown, + + gun_type, + 0::22 LR||1::223 Rem [AR-15], + + incident_characteristics, + "Shot - Dead (murder, accidental, suicide) + ||Mass Shooting (4+ victims injured or killed excluding the subject/suspect/perpetrator, one location) + ||Domestic Violence", + + latitude, location_description, longitude, + 34.9791,,-106.716, + + n_guns_involved, notes, + 2,, + + participant_age, + 0::51||1::40||2::9||3::5||4::2||5::15, + + participant_age_group, + 0::Adult 18+||1::Adult 18+||2::Child 0-11||3::Child 0-11||4::Child 0-11||5::Teen 12-17, + + participant_gender, + 0::Male||1::Female||2::Male||3::Female||4::Female||5::Male, + + participant_name, + 0::Greg Griego||1::Sara Griego||2::Zephania Griego||3::Jael Griego||4::Angelina Griego||5::Nehemiah Griego, + + participant_relationship, + 5::Family, + + participant_status, + "0::Killed||1::Killed||2::Killed||3::Killed||4::Killed||5::Unharmed, Arrested", + + participant_type, + 0::Victim||1::Victim||2::Victim||3::Victim||4::Victim||5::Subject-Suspect, + + http://www.cbsnews.com/news/nehemiah-gringo-case-memorial-service-planned-for-family-allegedly-slain-by-new-mexico-teen/|| + http://www.thewire.com/national/2013/01/teenager-reportedly-used-ar-15-kill-five-new-mexico/61199/|| + http://bigstory.ap.org/article/officials-nm-teen-gunman-kills-5-inside-home|| + http://www.huffingtonpost.com/2013/01/21/nehemiah-griego-teen-shoots-parents-3-children_n_2519359.html|| + http://murderpedia.org/male.G/g/griego-nehemiah.htm|| + http://hinterlandgazette.com/2013/01/pastor-greg-griego-identified-victims-killed-nehemiah-griego-albuquerque-nm-shooting.html, + 10,14 +*/ + +const fs = require('fs') +const parse = require('csv-parse') +const stringify = require('csv-stringify') + +const fields = ("incident_id,date,state,city_or_county," + + "address,n_killed,n_injured,incident_url,source_url," + + "incident_url_fields_missing,congressional_district," + + "gun_stolen,gun_type,incident_characteristics,latitude," + + "location_description,longitude,n_guns_involved,notes," + + "participant_age,participant_age_group,participant_gender," + + "participant_name,participant_relationship," + + "participant_status,participant_type," + + "sources,state_house_district,state_senate_district") + .split(',') + .reduce((a,b,i) => { + a[b] = i + return a + }, {}) + +const search = 'school-shootings' +// const regexp = new RegExp(search, 'i') + +function test(row){ + return ( + row + // && row[fields.incident_characteristics].indexOf('Non-Shooting Incident') === -1 + && row[fields.incident_characteristics].indexOf('School Shooting') !== -1 + // // && row[fields.participant_age_group].indexOf('Teen') !== -1 + // && row[fields.participant_age_group].indexOf('Child') !== -1 + // && row[fields.gun_type].indexOf('AR-15') !== -1 + ) +} + +const input = fs.createReadStream('./data/rest/gun_violence.csv') +const parser = parse() +const stringifier = stringify() +const output = fs.createWriteStream('./data/' + search + '.csv') + +input.on('readable', function() { + let buf + while ((buf = input.read()) !== null) { + parser.write(buf) + } +}) +input.on('error', function(err){ + console.error('input error', err.message) +}) +input.on('finish', function(){ + parser.end() +}) + +let i = 0 +parser.on('readable', function(){ + let row + while (row = parser.read()) { + if (i === 0) { + stringifier.write(row) + } + if ((++i % 10000) === 0) { + console.log(i + '...') + } + if (test(row)) { + stringifier.write(row) + } + } +}) +parser.on('error', function(err){ + console.error('parser error', err.message) +}) +parser.on('end', function(){ + stringifier.end() +}) + +stringifier.on('readable', function(){ + let row + while(row = stringifier.read()){ + output.write(row) + } +}) +stringifier.on('error', function(err){ + console.error('stringifier error', err.message) +}) +stringifier.on('finish', function(){ + output.end() +}) diff --git a/bin/group-mass.js b/bin/group-mass.js new file mode 100644 index 0000000..455f661 --- /dev/null +++ b/bin/group-mass.js @@ -0,0 +1,137 @@ + +/* + This script parses the mass shootings CSV and makes it easier to deal with. + + case, + Rite Aid warehouse shooting, + + location,date,summary, + "Perryman, MD", + 9/20/18, + "Snochia Moseley, 26, reportedly a disgruntled employee, shot her victims outside the building and on the warehouse floor; she later died from a self-inflicted gunshot at a nearby hospital. (No law enforcement officers responding to her attack fired shots.)", + + fatalities,injured,total_victims, + 3,3,6, + + location_type, + age_of_shooter, + Workplace, + 26, + + prior_signs_mental_health_issues,mental_health_details, + -,-, + + weapons_obtained_legally,where_obtained,weapon_type,weapon_details, + Yes,-,semiautomatic handgun,Glock 9 mm, + + race,gender, + Black,F, + + sources,mental_health_sources,sources_additional_age, + http://www.baltimoresun.com/news/maryland/crime/bs-md-harford-shooting-20180920-story.html; https://www.washingtonpost.com/local/public-safety/maryland-sheriffs-office-says-multiple-victims-in-shooting/2018/09/20/9d2c6464-bcda-11e8-be70-52bd11fe18af_story.html; https://www.cnn.com/2018/09/20/us/maryland-shooting/index.html; https://heavy.com/news/2018/09/snochia-moseley/,-,-, + + latitude,longitude,type,year + 39.455658,-76.208485,Mass,2018 +*/ + +const fs = require('fs') +const parse = require('csv-parse') +const stringify = require('csv-stringify') + +const fields = ( + "case," + + "location,date,summary,fatalities,injured,total_victims," + + "location_type," + + "age_of_shooter," + + "prior_signs_mental_health_issues,mental_health_details," + + "weapons_obtained_legally,where_obtained,weapon_type,weapon_details," + + "race,gender," + + "sources,mental_health_sources,sources_additional_age," + + "latitude,longitude,type,year").split(',').reduce((a,b,i) => { + a[b] = i + return a +}, {}) + +const headings = ["date", "timestamp", "fatalities", "injured", "total_victims", "age", "case", "weapon_type", "weapon_details"] + +function test(row){ + return ( + !!row + // row[fields.incident_characteristics].indexOf('Samaritan') !== -1 + // // && row[fields.participant_age_group].indexOf('Teen') !== -1 + // && row[fields.participant_age_group].indexOf('Child') !== -1 + // && row[fields.gun_type].indexOf('AR-15') !== -1 + ) +} + +function apply(row){ + let [m, d, y] = row[fields.date].split('/') + if (y < 20) y = parseInt(y) + 2000 + return [ + [y, pad(m), pad(d)].join('/'), + +new Date(y, m, d), + row[fields.fatalities], + row[fields.injured], + row[fields.total_victims], + row[fields.age_of_shooter], + row[fields.case], + row[fields.weapon_type].replace(/\s+$/g, ''), + row[fields.weapon_details].replace(/\s+$/g, ''), + ] +} +function pad(n) { return (n < 10) ? ("0" + n) : n } + +const input = fs.createReadStream('./data/mass_shootings.csv') +const parser = parse() +const stringifier = stringify() +const output = fs.createWriteStream('./data/mass_shootings_lite.csv') + +input.on('readable', function() { + let buf + while ((buf = input.read()) !== null) { + parser.write(buf) + } +}) +input.on('error', function(err){ + console.error('input error', err.message) +}) +input.on('finish', function(){ + parser.end() +}) + +let i = 0 +parser.on('readable', function(){ + let row + while (row = parser.read()) { + if (i === 0) { + stringifier.write(headings) + i += 1 + continue + } + if ((++i % 10000) === 0) { + console.log(i + '...') + } + if (test(row)) { + stringifier.write(apply(row)) + } + } +}) +parser.on('error', function(err){ + console.error('parser error', err.message) +}) +parser.on('end', function(){ + stringifier.end() +}) + +stringifier.on('readable', function(){ + let row + while(row = stringifier.read()){ + output.write(row) + } +}) +stringifier.on('error', function(err){ + console.error('stringifier error', err.message) +}) +stringifier.on('finish', function(){ + output.end() +}) diff --git a/bin/group.js b/bin/group.js new file mode 100644 index 0000000..fcd9424 --- /dev/null +++ b/bin/group.js @@ -0,0 +1,263 @@ + +/* + This script parses the big gun violence CSV and filters for lines containing a particular string. + + 479363, + 2013-01-19,New Mexico,Albuquerque,2806 Long Lane, + 5,0, + http://www.gunviolencearchive.org/incident/479363, + http://hinterlandgazette.com/2013/01/pastor-greg-griego-identified-victims-killed-nehemiah-griego-albuquerque-nm-shooting.html, + False,1, + + gun_stolen, + 0::Unknown||1::Unknown, + + gun_type, + 0::22 LR||1::223 Rem [AR-15], + + incident_characteristics, + "Shot - Dead (murder, accidental, suicide) + ||Mass Shooting (4+ victims injured or killed excluding the subject/suspect/perpetrator, one location) + ||Domestic Violence", + + latitude, location_description, longitude, + 34.9791,,-106.716, + + n_guns_involved, notes, + 2,, + + participant_age, + 0::51||1::40||2::9||3::5||4::2||5::15, + + participant_age_group, + 0::Adult 18+||1::Adult 18+||2::Child 0-11||3::Child 0-11||4::Child 0-11||5::Teen 12-17, + + participant_gender, + 0::Male||1::Female||2::Male||3::Female||4::Female||5::Male, + + participant_name, + 0::Greg Griego||1::Sara Griego||2::Zephania Griego||3::Jael Griego||4::Angelina Griego||5::Nehemiah Griego, + + participant_relationship, + 5::Family, + + participant_status, + "0::Killed||1::Killed||2::Killed||3::Killed||4::Killed||5::Unharmed, Arrested", + + participant_type, + 0::Victim||1::Victim||2::Victim||3::Victim||4::Victim||5::Subject-Suspect, + + http://www.cbsnews.com/news/nehemiah-gringo-case-memorial-service-planned-for-family-allegedly-slain-by-new-mexico-teen/|| + http://www.thewire.com/national/2013/01/teenager-reportedly-used-ar-15-kill-five-new-mexico/61199/|| + http://bigstory.ap.org/article/officials-nm-teen-gunman-kills-5-inside-home|| + http://www.huffingtonpost.com/2013/01/21/nehemiah-griego-teen-shoots-parents-3-children_n_2519359.html|| + http://murderpedia.org/male.G/g/griego-nehemiah.htm|| + http://hinterlandgazette.com/2013/01/pastor-greg-griego-identified-victims-killed-nehemiah-griego-albuquerque-nm-shooting.html, + 10,14 +*/ + +const fs = require('fs') +const parse = require('csv-parse') +const stringify = require('csv-stringify') + +const field_names = ( + "incident_id,date,state,city_or_county," + + "address,n_killed,n_injured,incident_url,source_url," + + "incident_url_fields_missing,congressional_district," + + "gun_stolen,gun_type,incident_characteristics,latitude," + + "location_description,longitude,n_guns_involved,notes," + + "participant_age,participant_age_group,participant_gender," + + "participant_name,participant_relationship," + + "participant_status,participant_type," + + "sources,state_house_district,state_senate_district" +) +const fields = field_names.split(',') + .reduce((a,b,i) => { + a[b] = i + return a + }, {}) + +const filter_names = [ + "Date", + "Children incidents", + "Children count", + "Teen incidents", + "Teen count", + "AR-15", + "Shootouts", + "Samaritans", + "Domestic Violence", + "Dead Cops", + "Perp Shot", + "Carjackings", + "School Shootings", + "Mass Shootings", + "Spree Shootings", + "Officer Involved", + "Surrenders", + "Dead Bystanders", +] +const filters = [ + (row, participants) => row[fields.participant_age_group].indexOf('Child') !== -1, + (row, participants) => participants[1].reduce((a, s) => { (s && s.indexOf('Child')) !== -1 ? a + 1 : a }, 0), + (row, participants) => row[fields.participant_age_group].indexOf('Teen') !== -1, + (row, participants) => participants[1].reduce((a, s) => { (s && s.indexOf('Teen')) !== -1 ? a + 1 : a }, 0), + (row, participants) => row[fields.gun_type].indexOf('AR-15') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Shootout') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Samaritan') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Domestic') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Officer shot') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('perpetrator shot') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Car-jack') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('School Shooting') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Mass Shooting') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Spree Shooting') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Officer Involved Shooting') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('surrender') !== -1, + (row, participants) => row[fields.incident_characteristics].indexOf('Bystander') !== -1, +] + +const groups = {} +const incident_counts = {} +for (let i = 2013; i <= 2018; i++) { + for (let j = 1; j <= 12; j++) { + let s = i + '-' + pad(j) + groups[s] = zeroes(filters.length + 1) + groups[s][0] = s + } +} + +function pad(n){ + if (n < 10) { + return '0' + n + } + return n +} +function test(row){ + return ( + row + && row[fields.incident_characteristics].indexOf('Non-Shooting Incident') === -1 + ) +} +function apply(row){ + const date = groupByMonth(row) + const p = groupParticipants(row) + + row[fields.incident_characteristics].split('|').forEach(s => { + if (!s) return + if (incident_counts[s]) { + incident_counts[s] += 1 + } else { + incident_counts[s] = 1 + } + }) + + filters.reduce((a, f, i) => { + a[i + 1] += Number(f(row, p)) + return a + }, groups[date]) +} +function groupParticipants(row){ + return [ + row[fields.participant_age], + row[fields.participant_age_group], + row[fields.participant_gender], + row[fields.participant_relationship], + row[fields.participant_status], + row[fields.participant_type], + ].map(field => field.split('||').map(s => s.split('::')[1])) +} +function groupByMonth(row){ + return row[fields.date].substr(0, 7) +} +function zeroes(n) { + const a = new Array (n) + for (var k = 0; k < n; k++) { + a[k] = 0 + } + return a +} +// function transform([ +// incident_id, date, state, city_or_county, +// address, n_killed, n_injured, incident_url, source_url, +// incident_url_fields_missing, congressional_district, +// gun_stolen, gun_type, incident_characteristics, latitude, +// location_description, longitude, n_guns_involved, notes, +// participant_age, participant_age_group, participant_gender, +// participant_name, participant_relationship, +// participant_status, participant_type, +// sources, state_house_district, state_senate_district +// ]){ +// return [ +// ] +// } + +const input = fs.createReadStream('./data/gun_violence.csv') +const parser = parse() + +const output = fs.createWriteStream('./data/gun_violence_by_month.csv') +const stringifier = stringify() +stringifier.pipe(output) + +const incident_output = fs.createWriteStream('./data/incident_counts.csv') +const incident_stringifier = stringify() +incident_stringifier.pipe(incident_output) + +input.on('readable', function() { + let buf + while ((buf = input.read()) !== null) { + parser.write(buf) + } +}) +input.on('error', function(err){ + console.error('input error', err.message) +}) +input.on('end', function(){ + parser.write(zeroes(fields.length)) + parser.end() +}) + +let count = 0 +parser.on('readable', function(){ + let row + while (row = parser.read()) { + if (count === 0) { + // stringifier.write(row) + count += 1 + continue + } + if ((++count % 50000) === 0) { + console.log(count + '...') + } + if (row[0] === 0) { + parser.end() + } else if (test(row)) { + apply(row) + } + } +}) +parser.on('error', function(err){ + console.error('parser error', err.message) +}) +parser.on('end', function(){ + console.log('parser end') + // console.log(groups) + stringifier.write(filter_names) + for (let i = 2013; i <= 2018; i++) { + for (let j = 1; j <= 12; j++) { + let s = i + '-' + pad(j) + stringifier.write(groups[s]) + } + } + stringifier.end() + + Object.keys(incident_counts) + .sort((a,b) => incident_counts[b] - incident_counts[a]) + .map(key => { + incident_stringifier.write([key, incident_counts[key]]) + if (incident_counts[key] > 5) { + console.log(incident_counts[key] + "\t" + key) + } + }) + incident_stringifier.end() +}) diff --git a/bin/media.js b/bin/media.js new file mode 100644 index 0000000..b8742ac --- /dev/null +++ b/bin/media.js @@ -0,0 +1,117 @@ +const fs = require('fs') +const parse = require('csv-parse') +const stringify = require('csv-stringify') + +const parklandInput = fs.createReadStream('./data/media_01_parkland_shooting.csv') +const averageInput = fs.createReadStream('./data/media_02_average_of_seven_shootings.csv') +const parklandParser = parse() +const averageParser = parse() +const stringifier = stringify() +const output = fs.createWriteStream('./data/media.csv') + +let finishCount = 0 +let parkland = [], average = [] +function finish() { + finishCount += 1 + console.log(finishCount) + if (finishCount < 2) { return } + console.log('finishing') + let a, b, nn + for (let n = 0; n < 12 * 8; n += 1) { + nn = n / 8 + a = find(nn, parkland) + b = find(nn, average) + stringifier.write([n, a, b]) + } + stringifier.end() +} + +/* + Given the sequence A of pairs (i,j) describing coordinates and an offset n, + find i where A[i][0] <= n and n < A[i+1][0] + and use these to interpolate the value of A(n) -> j or whatever you call it. +*/ +function find(n, a) { + let head, tail, dist + for (let i = 1, len = a.length; i < len; i++) { + if (a[i][0] > n) { + head = a[i-1] + tail = a[i] + dist = (n - head[0]) / (tail[0] - head[0]) + console.log(n, head, tail) + return lerp(dist, head[1], tail[1]) + } + } +} +function lerp(n, a, b) { + return (b-a) * n + a +} + +parklandInput.on('readable', function() { + let buf + while ((buf = parklandInput.read()) !== null) { + parklandParser.write(buf) + } +}) +parklandInput.on('error', function(err){ + console.error('parklandInput error', err.message) +}) +parklandInput.on('finish', function(){ + parklandParser.end() +}) + +averageInput.on('readable', function() { + let buf + while ((buf = averageInput.read()) !== null) { + averageParser.write(buf) + } +}) +averageInput.on('error', function(err){ + console.error('averageInput error', err.message) +}) +averageInput.on('finish', function(){ + console.log('enddd') + averageParser.end() +}) + +parklandParser.on('readable', function(){ + let row + while (row = parklandParser.read()) { + parkland.push(row.map(n => parseFloat(n))) + } + // console.log('p', parkland.length) + if (parkland.length == 108) finish() +}) +parklandParser.on('error', function(err){ + console.error('parklandParser error', err.message) +}) +parklandParser.on('end', function(){ + finish() +}) + +averageParser.on('readable', function(){ + let row + while (row = averageParser.read()) { + average.push(row.map(n => parseFloat(n))) + } + if (average.length == 89) finish() +}) +averageParser.on('error', function(err){ + console.error('averageParser error', err.message) +}) +averageParser.on('end', function(){ + finish() +}) + +stringifier.on('readable', function(){ + let row + while (row = stringifier.read()){ + output.write(row) + } +}) +stringifier.on('error', function(err){ + console.error('stringifier error', err.message) +}) +stringifier.on('finish', function(){ + output.end() +}) |
