/* 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() })