Skip to content

Instantly share code, notes, and snippets.

@ix4
Forked from sc1f/.block
Created April 7, 2020 00:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ix4/e97bd109b6d527fd9b5da9c3ba376b36 to your computer and use it in GitHub Desktop.
Save ix4/e97bd109b6d527fd9b5da9c3ba376b36 to your computer and use it in GitHub Desktop.
Perspective Workspace for COVID-19 U.S. Data
license: apache-2.0
height: 800

COVID-19 Perspective Workspace

This perspective-workspace uses perspective-python to host the dataset in a remote server. When you load the page, the datset is serialized to Apache Arrow and streamed to the browser.

<!DOCTYPE html>
<html>
<head>
<title>COVID-19 Perspective Workspace</title>
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<script src="https://perspective-covid.herokuapp.com/static/perspective-workspace/umd/perspective-workspace.js">
</script>
<script
src="https://perspective-covid.herokuapp.com/static/perspective-viewer-datagrid/umd/perspective-viewer-datagrid.js">
</script>
<script src="https://perspective-covid.herokuapp.com/static/perspective-viewer-d3fc/umd/perspective-viewer-d3fc.js">
</script>
<script src="https://perspective-covid.herokuapp.com/static/perspective/umd/perspective.js"></script>
<link rel="stylesheet"
href="https://perspective-covid.herokuapp.com/static/perspective-workspace/umd/material.css" />
<style>
body {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<perspective-workspace id="workspace"></perspective-workspace>
<script>
const worker = perspective.shared_worker();
const state_data = async websocket => {
// Get a proxy for a view named "state_data_source", registered on
// the server with a reciprocal call to `host_view()`.
// No data is transferred, `view` is a virtual handle for data on
// the server.
const view = websocket.open_view("state_data_source");
// Create a `table` from this, owned by the local WebWorker.
// Data is transferred from `view` to the local WebWorker, both
// the current state and all future updates, as Arrows.
return worker.table(view);
};
const county_data = async websocket => {
// Get a proxy for a view named "state_data_source", registered on
// the server with a reciprocal call to `host_view()`.
// No data is transferred, `view` is a virtual handle for data on
// the server.
const view = websocket.open_view("county_data_source");
// Create a `table` from this, owned by the local WebWorker.
// Data is transferred from `view` to the local WebWorker, both
// the current state and all future updates, as Arrows.
return worker.table(view);
};
window.addEventListener("load", async () => {
const websocket = perspective.websocket(
"wss://perspective-covid.herokuapp.com/ws"
);
window.workspace.addTable("state", state_data(websocket));
window.workspace.addTable("county", county_data(websocket));
const line_config = window.getPlugin("d3_y_line");
line_config.max_cells = 200000;
line_config.max_columns = 7500;
await window.workspace.restore({
sizes: [0.23643658663883088, 0.7635634133611691],
detail: {
main: {
type: "split-area",
orientation: "horizontal",
children: [
{
type: "tab-area",
widgets: [
"CountyCasesWidget"
],
currentIndex: 0
},
{
type: "split-area",
orientation: "vertical",
children: [{
type: "tab-area",
widgets: ["CountyScatterWidget"],
currentIndex: 0
},
{
type: "tab-area",
widgets: ["CountyPopulationWidget"],
currentIndex: 0
}
],
sizes: [0.5, 0.5]
},
],
sizes: [0.5, 0.5]
}
},
master: {
widgets: ["MasterStateWidget", "MasterDateWidget"]
},
mode: "globalFilters",
viewers: {
MasterStateWidget: {
plugin: "datagrid",
name: "Cases & Deaths by State",
table: "state",
"computed-columns": [
"'New Deaths' % 'New Cases' as 'Fatality Rate (%)'"
],
columns: [
"Fatality Rate (%)",
"New Cases",
"New Deaths"
],
"row-pivots": ["State Name"],
aggregates: {
"Cumulative Cases": "high",
"Cumulative Deaths": "high",
"Fatality Rate (%)": "avg",
"Date": "dominant"
},
sort: [
["Cumulative Cases", "desc"]
]
},
MasterDateWidget: {
plugin: "datagrid",
name: "Cases & Deaths by Date",
table: "state",
"computed-columns": [
"'New Deaths' % 'New Cases' as 'Fatality Rate (%)'"
],
columns: [
"Fatality Rate (%)",
"New Cases",
"New Deaths"
],
sort: [
["Date", "desc"]
],
"row-pivots": ["Date"],
aggregates: {
"Cumulative Cases": "high",
"Cumulative Deaths": "high",
"Fatality Rate (%)": "avg",
Date: "dominant"
}
},
CountyCasesWidget: {
plugin: "d3_y_line",
name: "Cases since March 1st",
table: "county",
columns: ["Cumulative Cases"],
"row-pivots": ["Date"],
"column-pivots": ["Location"],
aggregates: {
"Date": "dominant",
"Cumulative Cases": "high",
"Cumulative Deaths": "high",
"Population (2018 Estimate)": "high",
"Unemployment Rate % (2018 Estimate)": "high",
"Unemployed (2018 Estimate)": "high",
"Employed (2018 Estimate)": "high",
"Civilian Labor Force (2018 Estimate)": "high",
"Median Household Income (2018 Estimate)": "high",
},
"computed-columns": [
'concat_comma("County", "State") as "Location"'
],
plugin_config: {
legend: {
left: "80px",
top: "15px"
}
}
},
CountyScatterWidget: {
plugin: "d3_xy_scatter",
name: "Cases & Deaths Scatter Plot (by county)",
table: "county",
columns: ["New Cases", "New Deaths"],
"row-pivots": ["Location"],
aggregates: {
"Date": "dominant",
"Cumulative Cases": "high",
"Cumulative Deaths": "high",
"Population (2018 Estimate)": "high",
"Unemployment Rate % (2018 Estimate)": "high",
"Unemployed (2018 Estimate)": "high",
"Employed (2018 Estimate)": "high",
"Civilian Labor Force (2018 Estimate)": "high",
"Median Household Income (2018 Estimate)": "high",
},
"computed-columns": [
'concat_comma("County", "State") as "Location"'
]
},
CountyPopulationWidget: {
name: "Cases/Deaths with county-level population, income, unemployment data",
table: "county",
plugin: "datagrid",
columns: [
"Cases % Population",
"New Cases",
"New Deaths",
"Population (2018 Estimate)",
"Unemployed (2018 Estimate)",
"Employed (2018 Estimate)",
"Civilian Labor Force (2018 Estimate)",
"Median Household Income (2018 Estimate)",
],
sort: [["Population (2018 Estimate)", "desc"]],
aggregates: {
"Cases % Population": "avg",
"Date": "dominant",
"Cumulative Cases": "high",
"Cumulative Deaths": "high",
"Population (2018 Estimate)": "high",
"Unemployment Rate % (2018 Estimate)": "high",
"Unemployed (2018 Estimate)": "high",
"Employed (2018 Estimate)": "high",
"Civilian Labor Force (2018 Estimate)": "high",
"Median Household Income (2018 Estimate)": "high",
},
"row-pivots": ["Location"],
"computed-columns": [
'concat_comma("County", "State") as "Location"',
"'New Cases' % 'Population (2018 Estimate)' as 'Cases % Population'"
]
},
}
});
// Custom formatting for the datagrid - display percentages with a `%`
for (const viewer of document
.getElementById("workspace")
.querySelectorAll("perspective-viewer")) {
viewer.addEventListener(
"perspective-datagrid-after-update",
event => {
const datagrid = event.detail;
for (const td of datagrid.get_tds()) {
const metadata = datagrid.get_meta(td);
if (
metadata.column.includes("%") &&
typeof metadata.value === "number"
) {
const pct = metadata.value
.toFixed(4)
.toString();
td.textContent = pct + "%";
} else if (metadata.column === "Median Household Income (2018 Estimate)") {
td.textContent = "$" + metadata.value;
} else {
td.style.display = "table-cell";
}
}
}
);
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment