Public
Edited
Jul 22, 2024
Importers
Insert cell
Insert cell
aa_poetry_data = d3.csv("https://raw.githubusercontent.com/melaniewalsh/responsible-datasets-in-context/main/datasets/aa-periodical-poetry/AAPADA-Periodical-Poetry_1900-1928.csv", d3.autoType)
Insert cell
formatted_poetry_data = aa_poetry_data.map(d => {
return Object.fromEntries(
Object.entries(d).map(([key, value]) => [
key,
value === null || value === undefined || value === "" ? null : key === "year" ? new Date(value, 0, 1) : value
])
);
});
Insert cell
import { SmallStack, Histogram, SummaryCard, getDateFormat, dateFormat, getType, icon_fns, pct_format } from "@observablehq/summary-table"
Insert cell
// A function to summarize a single column
SummarizeColumn = (data, col) => {
let content,
value,
format,
finiteFormat,
el,
chart,
missing_label,
pct_missing,
min,
max;
const notFiniteFormat = d3.format(",.0f");
const pct_format = d3.format(".2%"); // Ensure the percentage format is defined

// Construct content based on type
const type = getType(data, col);

const col1 = htl.html`<td style="white-space: nowrap;vertical-align:middle;padding-right:5px;padding-left:3px;">${icon_fns[
type
]()}<strong style="vertical-align:middle;">${
col === "" ? "unlabeled" : col
}</strong></td>`;

const missingCount = data.filter(
(d) => d[col] === null || d[col] === ""
).length;
pct_missing = (missingCount / data.length) * 100;

switch (type) {
// Categorical columns
case "ordinal":
format = d3.format(",.0f");

// Calculate category percent and count
const categories = d3
.rollups(
data.filter((d) => d[col] !== null && d[col] !== ""),
(v) => ({ count: v.length, pct: v.length / data.length || 1 }),
(d) => d[col]
)
.sort((a, b) => b[1].count - a[1].count)
.map((d) => {
let obj = {};
obj[col] = d[0];
obj.count = d[1].count;
obj.pct = d[1].pct;
return obj;
});

// Create the chart
const stack_chart = SmallStack(categories, col);

// element to return
el = htl.html`<tr style="font-family:sans-serif;font-size:13px;">
${col1}
<td><div style="position:relative;">${stack_chart}</div></td>
<td>${pct_format(
pct_missing / 100
)}</td> <!-- Convert percentage to decimal for formatting -->
</tr>`;

value = {
column: col,
type,
min: null,
max: null,
missing: pct_missing,
n_categories: categories.length
};
break;

// Date columns
case "date":
// Calculate and format start / end
const start = d3.min(data, (d) => +d[col]);
const end = d3.max(data, (d) => +d[col]);

chart = Histogram(data, col, type);

// Element to return
el = htl.html`<tr style="font-family:sans-serif;font-size:13px;">
${col1}
<td><div style="position:relative;">${chart}</div></td>
<td>${pct_format(pct_missing / 100)}</td>
</tr>`;
value = {
column: col,
type,
min: start,
max: end,
missing: pct_missing,
n_categories: null
};
break;

// Continuous columns
default:
// Compute values
min = d3.min(data, (d) => +d[col]);
max = d3.max(data, (d) => +d[col]);

chart = Histogram(data, col, type);
// Element to return
el = htl.html`<tr style="font-family:sans-serif;font-size:13px;">
${col1}
<td><div style="position:relative;top:3px;">${chart}</div></td>
<td>${pct_format(pct_missing / 100)}</td>
</tr>`;

value = {
column: col,
type,
min,
max,
missing: pct_missing,
n_categories: null
};
break;
}
el.value = value;
el.appendChild(html`<style>td {vertical-align:middle;} </style>`);
return el;
}
Insert cell
SummaryTable = (dataObj, {label="Summary"} = {}) => {
const data = typeof dataObj.numRows === "function" ? dataObj.objects() :
typeof dataObj.toArray === "function" ? dataObj.toArray().map((r) => Object.fromEntries(r)) :
dataObj
const sample = data[0] || {};
const cols = data.columns || Object.keys(sample);
let value = []

// Create the summary card and track data shape
const summaryCard = SummaryCard(data, label)
value.n_rows = summaryCard.value.n_rows
value.n_columns = summaryCard.value.n_columns
value.columns = cols

// Compose the element
const element = htl.html`<div style="display:inline-block; vertical-align:top;">${summaryCard}</div>
<div style="display:inline-block; max-width:${width < 500 ? width : width - 160}px">
<table style="vertical-align:middle; display:block;overflow-x:auto; max-width:${width}px;">
<thead style="z-index:-999;">
<th>Column</th>
<th style="min-width:250px">Snapshot</th>
<th>Missing</th>
</thead>
${cols.map(d => {
const ele = SummarizeColumn(data, d)
value.push(ele.value) // get the value from the element
return ele
})}
</table>
</div>`
element.value = value;
return element
}
Insert cell
viewof new_summary_data = SummaryTable(formatted_poetry_data, {
label: "AA Poetry Data"
});
Insert cell
Select a data source…
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more