Skip to content

Commit

Permalink
more examples, better presentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Fil committed Mar 25, 2024
1 parent be8440b commit 217acd7
Showing 1 changed file with 71 additions and 28 deletions.
99 changes: 71 additions & 28 deletions docs/summary-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ SELECT * FROM gaia;
SELECT * FROM penguins;
```

```sql echo
SELECT a::int
, b::int
, (a * b)::int as p
FROM generate_series(1, 9) as s(a)
, generate_series(1, 9) as t(b);
```

```sql echo
FROM range(10, 22, 2);
```

```sql echo
SELECT range * pi() as pi FROM range(10);
```

```sql echo
SELECT cos(range * pi() / 10) as x, sin(range * pi() / 10) as y FROM range(0, 20, 1);
```

```js echo
const sql = DuckDBClient.sql({aapl, penguins, gaia: FileAttachment("/lib/gaia-sample.parquet")});
```
Expand All @@ -36,7 +56,7 @@ function table(data, options = {}) {
if (!Array.isArray(data?.schema?.fields)) return container;

// Get the fields as described by Arrow, in the order given (potentially) by the options.
const fields = (options.columns?.map(k => data.schema.find(({name}) => name === k)) ?? data.schema.fields).map(({name, type}) => ({name: String(name), type: String(type)}));
const fields = (options.columns?.map(k => data.schema.find(({name}) => name === k)) ?? data.schema.fields).map(({name, type}) => ({name: String(name), type: String(type), values: data.getChild(name)}));

const th = d3.select(container).select("thead").selectAll("th").data([{}, ...fields]);
th.append("div").classed("type", true).html(({type}) => type);
Expand All @@ -47,61 +67,84 @@ function table(data, options = {}) {
</footer>`;
container.appendChild(footer);

requestAnimationFrame(() => summaries
.filter(({type}) => type)
.append(function({name, type}) {
return summary(data.getChild(name), type, this.getBoundingClientRect());
})
);
requestAnimationFrame(() => summaries.filter(({type}) => type).append(summary));
return container;
}

function summary(values, type, {width = 80, height = 33}) {
function summary({name, type, values}) {
const {width: w, height} = this.getBoundingClientRect();
const width = Math.min(200, (w ?? 80) - 10);
let chart;
if (type.startsWith("Float") || type.startsWith("Date")) {

// Count values, NaN, nulls, distinct
// TODO use DuckdB?
let max = -Infinity;
let min = Infinity;
let nulls = 0;
const distinct = new Set();
const capped = 100; // max number of distinct values to count
for (const v of values) {
if (v == null) {nulls++; continue;}
if (min > v) min = v; // note this works for strings too
if (max < v) max = v;
if (distinct.size <= capped && !distinct.has(v)) distinct.add(v);
}

if (distinct.size <= 10 || type === "Utf8") {
const stackOptions = (type === "Utf8") ? {order: "sum", reverse: true} : {order: "value"};
chart = Plot.plot({
width,
height,
style: "overflow: visible;",
x: {round: true},
x: {axis: null},
y: {axis: null},
marginLeft: 2,
marginRight: 2,
marginTop: 0,
marginBottom: 13,
marks: [
Plot.rectY(values, Plot.binX(undefined, {fill: "var(--theme-foreground-focus)", inset: -.3})),
Plot.axisX({tickSpacing: 41, tickSize: 3, tickPadding: 2, fontSize: 8}),
Plot.barX(values, Plot.stackX(stackOptions, Plot.groupZ({x: "count"}, {z: Plot.identity, insetRight: 1, fill: "var(--theme-foreground-focus)"}))),
Plot.text(values, Plot.stackX(stackOptions, Plot.groupZ({x: "count", text: "first"}, {z: Plot.identity, fill: "var(--plot-background)"}))),
]
});

// restore insets where possible
const rects = chart.querySelectorAll("rect");
if (rects.length < 100) {
for (const rect of rects) {
rect.setAttribute("x", Math.floor(rect.getAttribute("x")));
rect.setAttribute("width", Math.max(1, Math.floor(rect.getAttribute("width")) - 1));
}
}
}
else if (type === "Utf8") {
else {
const thresholds = Math.max(10, Math.min(50, d3.thresholdScott(values, min, max))); // TODO optimize thresholdScott
chart = Plot.plot({
width,
height,
style: "overflow: visible;",
x: {axis: null},
x: {
round: true,
nice: true
},
y: {axis: null},
marginLeft: 2,
marginRight: 2,
marginLeft: 9,
marginRight: 9,
marginTop: 0,
marginBottom: 13,
marks: [
Plot.barX(values, Plot.groupZ({x: "count"}, {z: Plot.identity, insetRight: 1, fill: "var(--theme-foreground-focus)"})),
Plot.text(values, Plot.stackX(Plot.groupZ({x: "count", text: "first"}, {z: Plot.identity, fill: "var(--plot-background)"}))),
thresholds > 20 ?
Plot.areaY(values, Plot.binX(undefined, {
fill: "var(--theme-foreground-focus)",
thresholds
})) :
Plot.rectY(values, Plot.binX(undefined, {
fill: "var(--theme-foreground-focus)",
thresholds,
inset: 0,
insetRight: 1,
})),
min * max <= 0 ? Plot.ruleX([0]) : [],
Plot.ruleY([0]),
Plot.axisX({tickSpacing: 41, tickSize: 3, tickPadding: 2, fontSize: 8, ...(!type.startsWith("Date") && Math.max(Math.abs(min), Math.abs(max)) >= 1e5 && {tickFormat: "s"})}),
]
});
}
return chart ?? html`<span>Unknown type ${type}`;
return chart ? html`<div style=${type === "Utf8" ? "" : {
position: "absolute",
right: 0
}}>${chart}` : html`<span>Unknown type ${type}`;
}
```
Expand Down

0 comments on commit 217acd7

Please sign in to comment.