Skip to content

Instantly share code, notes, and snippets.

@ix4
Forked from sc1f/.block
Created April 7, 2020 00:39
Show Gist options
  • 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>
<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 state_data = async (websocket) => {
const worker = perspective.worker();
// 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) => {
const worker = perspective.worker();
// 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");
const state_datasource = await state_data(websocket);
const county_datasource = await county_data(websocket)
window.workspace.tables.set("state", state_datasource);
window.workspace.tables.set("county", county_datasource);
// const scatter_config = window.getPlugin("d3_xy_scatter");
// scatter_config.max_columns = 500;
// const area_config = window.getPlugin("d3_y_area");
// area_config.max_columns = 500;
// area_config.max_cells = 100000;
const line_config = window.getPlugin("d3_y_line");
line_config.max_cells = 100000;
line_config.max_columns = 5000;
await window.workspace.restore({
sizes: [0.25, 0.75],
detail: {
main: {
type: "split-area",
orientation: "vertical",
children: [
{
type: "split-area",
orientation: "horizontal",
children: [
{
type: "tab-area",
widgets: ["StateConfirmedWidget", "StatePopulationWidget"],
currentIndex: 0
},
{
type: "tab-area",
widgets: ["StateDeathsWidget"],
currentIndex: 0
}
],
sizes: [0.5, 0.5]
},
{
type: "split-area",
orientation: "horizontal",
children: [
{
type: "tab-area",
widgets: ["CountyGridWidget"],
currentIndex: 0
},
{
type: "tab-area",
widgets: ["CountyConfirmedWidget"],
currentIndex: 0
},
{
type: "tab-area",
widgets: ["CountyDeathsWidget"],
currentIndex: 0
}
],
sizes: [0.33, 0.33, 0.33]
}
],
sizes: [0.5, 0.5]
}
},
master: {
widgets: ["MasterStateWidget", "MasterDateWidget"]
},
mode: "globalFilters",
viewers: {
MasterStateWidget: {
plugin: "datagrid",
name: "Confirmed & Deaths by State",
table: "state",
"computed-columns": ["'Deaths' % 'Confirmed' as 'Fatality Rate (%)'"],
columns: ["Fatality Rate (%)", "Confirmed", "Deaths"],
"row-pivots": ["State Name"],
aggregates: {
"Confirmed": "high",
"Deaths": "high",
"Fatality Rate (%)": "avg",
"Date": "dominant"
},
sort: [["Confirmed", "desc"]],
},
MasterDateWidget: {
plugin: "datagrid",
name: "Confirmed & Deaths by Date",
table: "state",
"computed-columns": ["'Deaths' % 'Confirmed' as 'Fatality Rate (%)'"],
columns: ["Fatality Rate (%)", "Confirmed", "Deaths"],
"row-pivots": ["Date"],
aggregates: {
"Confirmed": "high",
"Deaths": "high",
"Fatality Rate (%)": "avg",
"Date": "dominant"
},
sort: [["Confirmed", "desc"]],
},
StateConfirmedWidget: {
name: "Cases since March 1st (by state)",
table: "state",
columns: ["Confirmed"],
"row-pivots": ["Date"],
"filters": [["Date", ">", "03/01/2020"]],
"column-pivots": ["State Name"],
plugin: "d3_y_line",
plugin_config: {
legend: {
left: "80px",
top: "15px"
}
},
},
StateDeathsWidget: {
name: "Deaths since March 1st (by state)",
table: "state",
columns: ["Deaths"],
"row-pivots": ["Date"],
"filters": [["Date", ">", "03/01/2020"]],
"column-pivots": ["State Name"],
plugin: "d3_y_line",
plugin_config: {
legend: {
left: "80px",
top: "15px"
}
},
},
StatePopulationWidget: {
name: "Cases as % of state population",
table: "state",
plugin: "datagrid",
columns: ["Cases % Population", "Confirmed", "Population (2019 Estimate)"],
aggregates: {
"Confirmed": "high",
"Cases % Population": "avg",
"Population (2019 Estimate)": "high"
},
"row-pivots": ["State Name"],
"computed-columns": ["'Confirmed' % 'Population (2019 Estimate)' as 'Cases % Population'"]
},
CountyConfirmedWidget: {
name: "Cases since March 1st (by county)",
table: "county",
columns: ["Confirmed"],
"row-pivots": ["Date"],
"column-pivots": ["Location"],
"filters": [["Date", ">", "03/01/2020"]],
plugin: "d3_y_line",
"computed-columns": ['concat_comma("County", "State") as "Location"', "'Deaths' % 'Confirmed'"],
plugin_config: {
legend: {
left: "80px",
top: "15px"
}
},
"computed-columns": ['concat_comma("County", "State") as "Location"'],
},
CountyDeathsWidget: {
name: "Deaths since March 1st (by county)",
table: "county",
columns: ["Deaths"],
"row-pivots": ["Date"],
"column-pivots": ["Location"],
"filters": [["Date", ">", "03/01/2020"]],
plugin: "d3_y_line",
"computed-columns": ['concat_comma("County", "State") as "Location"', "'Deaths' % 'Confirmed'"],
plugin_config: {
legend: {
left: "80px",
top: "15px"
}
},
"computed-columns": ['concat_comma("County", "State") as "Location"'],
},
CountyGridWidget: {
plugin: "datagrid",
name: "Confirmed & Deaths (by county)",
table: "county",
columns: ["Confirmed", "Deaths"],
"row-pivots": ["Location"],
sort: [["Confirmed", "desc"]],
aggregates: {
"Confirmed": "high",
"Deaths": "high"
},
"computed-columns": ['concat_comma("County", "State") as "Location"'],
},
}
});
// 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 {
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