Every week, at least hundreds (if not thousands) of people will be affected by data breaches at medical facilities. When these events affect more than 500 people, they are reported to the Health and Human Services website, here. As of today (07/07/19), there are 493 open cases going back to July 2017, and 2288 cases in the archive going back to October 2009. I've attached the CSV data export to this block for convenience, but you could go to the portal and upload more recent data if you'd like.
John Guerra Gómez created the navio d3 plugin called that is suitable for exploring datasets with multiple quantitive/categorical columns that I had been meaning to try. Once I realized that there weren't any bl.ocks.org
examples with Navio
in the title, I decided to put these two interesting resources together.
For doing quick data exploration from the command line, I recommend Visidata.
Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="//unpkg.com/d3@4.13.0/build/d3.min.js"></script>
<script src="//code.jquery.com/jquery-3.3.1.js"> </script>
<script src="//d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="//unpkg.com/popper.js@1.14/dist/umd/popper.min.js"></script>
<script src="//unpkg.com/navio@0.0.58/dist/navio.js"></script>
<script src="//unpkg.com/humanize-plus@1.8.2/dist/humanize.min.js"></script>
<script src="//cdn.datatables.net/1.10.19/js/jquery.datatables.min.js"> </script>
<link href="//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" rel="stylesheet" type="text/css">
<style>
body { position:fixed;top:0;right:0;bottom:0;left:0; }
#navio-anchor {
margin-top: 80px;
}
.stats-container {
margin: 20px 30px;
}
</style>
</head>
<body>
<div class="stats-container">
<p id='summary-stats'></p>
</div>
<div id="navio-anchor"></div>
<div class="datatable-container">
<table id='datatable' class="display"></table>
</div>
<script>
$(window).on("load", function() {
const HEIGHT = 450; // in pixels
const parseTime = d3.timeParse("%m/%d/%Y"); // 12/31/2018
const formatTime = d3.timeFormat("%m/%d/%Y");
d3.csv("./breach_report.csv", (err, rows) => {
if (err) throw err;
// Light data postprocessing
// Keep select columns, convert key names to camelcase,
// and coerce appropriate datatypes.
const processedRows = rows.map(row => ({
submission_date: parseTime(row["Breach Submission Date"]),
state: row["State"],
name_of_covered_entity: row["Name of Covered Entity"],
covered_entity_type: row["Covered Entity Type"],
individuals_affected: +row["Individuals Affected"],
location_of_breached_information: row["Location of Breached Information"],
was_business_associate_present:
row["Business Associate Present"] === "Yes"
}));
// Create the navio object and provide it with suitable schema
const nv = navio(d3.select("#navio-anchor"), HEIGHT)
.addCategoricalAttrib("covered_entity_type")
.addOrderedAttrib("state")
.addTextAttrib("name_of_covered_entity")
.addSequentialAttrib("individuals_affected")
.addDateAttrib("submission_date")
.addBooleanAttrib("was_business_associate_present")
.addOrderedAttrib("location_of_breached_information");
nv.data(processedRows);
nv.update(); // initial draw
// Then, make a datatable to see the rows up-close.
const tableKeys = [
"submission_date",
"state",
"name_of_covered_entity",
"covered_entity_type",
"individuals_affected",
"location_of_breached_information",
"was_business_associate_present"
];
// Transformations just for the DataTable, which expects
// a list of lists of data objects in the order of the tableKeys.
const objectToDataTableRow = row => {
return tableKeys.map(key => {
return key === "submission_date" ? formatTime(row[key]) : row[key];
});
};
const tableRows = processedRows.map(objectToDataTableRow);
const tableSettings = {
columns: tableKeys.map(title => ({ title })),
scrollY: "35vh",
scrollCollapse: true,
paging: true
};
const dataTable = $("#datatable").DataTable({
data: tableRows,
...tableSettings
});
// script to update the stats box
const updateStatsBox = selection => {
const numAffected = selection.reduce(
(acc, cur) => acc + cur.individuals_affected,
0
);
const dateRange = d3
.extent(selection, d => d.submission_date)
.map(formatTime)
.join(" to ");
const message = `About ${Humanize.compactInteger(
numAffected,
2
)} individuals were affected by ${
selection.length
} medical data breaches currently under investigation from ${dateRange}. Click and drag to filter the breach events.`;
$("#summary-stats").text(message);
};
updateStatsBox(processedRows);
// Lastly, make the selected rows available in the data table!
// It would be more efficient do this this reactively, but
// I'm trying to restrict this example to a single file.
nv.updateCallback(selection => {
dataTable.clear();
dataTable.rows.add(selection.map(objectToDataTableRow)).draw();
updateStatsBox(selection);
});
});
});
</script>
</body>
Updated missing url //cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js to //cdn.datatables.net/1.10.19/js/jquery.datatables.min.js
https://unpkg.com/d3@4.13.0/build/d3.min.js
https://code.jquery.com/jquery-3.3.1.js
https://d3js.org/d3-scale-chromatic.v1.min.js
https://unpkg.com/popper.js@1.14/dist/umd/popper.min.js
https://unpkg.com/navio@0.0.58/dist/navio.js
https://unpkg.com/humanize-plus@1.8.2/dist/humanize.min.js
https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js