Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions playground/apply.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>tsb — apply / map</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 860px; margin: 2rem auto; padding: 0 1rem; background: #fafafa; color: #1a1a1a; }
h1 { font-size: 1.6rem; }
h2 { font-size: 1.2rem; margin-top: 2rem; border-bottom: 1px solid #ddd; padding-bottom: 0.3rem; }
pre { background: #f0f0f0; border-radius: 6px; padding: 1rem; overflow-x: auto; font-size: 0.85rem; }
.demo { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; padding: 1rem 1.5rem; margin: 1rem 0; }
button { margin-top: 0.5rem; padding: 0.4rem 1rem; cursor: pointer; border-radius: 4px; border: 1px solid #aaa; background: #f5f5f5; }
button:hover { background: #e8e8e8; }
output { display: block; margin-top: 0.5rem; white-space: pre; font-family: monospace; font-size: 0.85rem; color: #333; }
.label { font-weight: 600; font-size: 0.85rem; color: #555; margin-top: 0.8rem; }
textarea { width: 100%; box-sizing: border-box; font-family: monospace; font-size: 0.85rem; border: 1px solid #ccc; border-radius: 4px; padding: 0.5rem; }
.pandas-equiv { background: #fff8e1; border-left: 3px solid #f9a825; padding: 0.4rem 0.8rem; margin-top: 0.5rem; font-size: 0.8rem; border-radius: 0 4px 4px 0; }
</style>
</head>
<body>
<h1>tsb — <code>apply</code> / <code>map</code></h1>
<p>
Apply functions element-wise or per-column/row.
<code>applySeries</code> maps a function over each element.
<code>mapSeries</code> supports function, <code>Map</code>, or plain-object lookup.
<code>applyDataFrame</code> reduces each column or row to a scalar.
<code>applyExpandDataFrame</code> transforms each column/row into a new Series.
<code>mapDataFrame</code> applies a function element-wise across the whole DataFrame.
</p>

<h2>Core concept</h2>
<pre>// Element-wise apply on a Series
applySeries(s, (v) => Math.sqrt(v as number))

// Map via lookup table
mapSeries(s, { a: 1, b: 2, c: 3 })

// Reduce each column to a scalar
applyDataFrame(df, (col) => col.values.reduce((a, b) => a + b, 0))

// Transform each column, return a DataFrame
applyExpandDataFrame(df, (col) => new Series({ data: col.values.map(v => v * 2), index: col.index }))

// Element-wise map on a DataFrame
mapDataFrame(df, (v) => (v as number) ** 2)</pre>

<div class="pandas-equiv">
<strong>pandas equivalent:</strong><br />
<code>s.apply(func)</code> / <code>s.map(func_or_dict)</code><br />
<code>df.apply(func, axis=0)</code> / <code>df.applymap(func)</code> (now <code>df.map(func)</code>)
</div>

<!-- Demo 1: applySeries -->
<h2>Demo 1 — applySeries element-wise</h2>
<div class="demo">
<div class="label">Code</div>
<pre>const s = new Series({ data: [1, 4, 9, 16] });
applySeries(s, (v) => Math.sqrt(v as number)).values;
// → [1, 2, 3, 4]</pre>
<button onclick="demo1()">Run</button>
<output id="out1"></output>
</div>

<!-- Demo 2: mapSeries with lookup -->
<h2>Demo 2 — mapSeries with object lookup</h2>
<div class="demo">
<div class="label">Code</div>
<pre>const s = new Series({ data: ["a", "b", "c", "d"] });
mapSeries(s, { a: 1, b: 2, c: 3 }).values;
// → [1, 2, 3, null] ("d" not in lookup → null)</pre>
<button onclick="demo2()">Run</button>
<output id="out2"></output>
</div>

<!-- Demo 3: applyDataFrame axis=0 -->
<h2>Demo 3 — applyDataFrame: sum of each column (axis=0)</h2>
<div class="demo">
<div class="label">Code</div>
<pre>const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [10, 20, 30] });
applyDataFrame(df, (col) =>
(col.values as number[]).reduce((acc, v) => acc + v, 0)
).values;
// → [6, 60] (indexed by column names)</pre>
<button onclick="demo3()">Run</button>
<output id="out3"></output>
</div>

<!-- Demo 4: applyDataFrame axis=1 -->
<h2>Demo 4 — applyDataFrame: sum of each row (axis=1)</h2>
<div class="demo">
<div class="label">Code</div>
<pre>const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [4, 5, 6] });
applyDataFrame(df, (row) =>
(row.values as number[]).reduce((acc, v) => acc + v, 0),
{ axis: 1 }
).values;
// → [5, 7, 9]</pre>
<button onclick="demo4()">Run</button>
<output id="out4"></output>
</div>

<!-- Demo 5: applyExpandDataFrame -->
<h2>Demo 5 — applyExpandDataFrame: double each column</h2>
<div class="demo">
<div class="label">Code</div>
<pre>const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [4, 5, 6] });
applyExpandDataFrame(df, (col) =>
new Series({ data: (col.values as number[]).map(v => v * 2), index: col.index })
);
// a: [2, 4, 6] b: [8, 10, 12]</pre>
<button onclick="demo5()">Run</button>
<output id="out5"></output>
</div>

<!-- Demo 6: mapDataFrame -->
<h2>Demo 6 — mapDataFrame: element-wise square</h2>
<div class="demo">
<div class="label">Code</div>
<pre>const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [4, 5, 6] });
mapDataFrame(df, (v) => (v as number) ** 2);
// a: [1, 4, 9] b: [16, 25, 36]</pre>
<button onclick="demo6()">Run</button>
<output id="out6"></output>
</div>

<script type="module">
import {
Series,
DataFrame,
applySeries,
mapSeries,
applyDataFrame,
applyExpandDataFrame,
mapDataFrame,
} from "https://esm.sh/tsb@latest";

window.demo1 = () => {
const s = new Series({ data: [1, 4, 9, 16] });
document.getElementById("out1").textContent =
JSON.stringify(applySeries(s, (v) => Math.sqrt(v)).values);
};

window.demo2 = () => {
const s = new Series({ data: ["a", "b", "c", "d"] });
document.getElementById("out2").textContent =
JSON.stringify(mapSeries(s, { a: 1, b: 2, c: 3 }).values);
};

window.demo3 = () => {
const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [10, 20, 30] });
const result = applyDataFrame(df, (col) =>
col.values.reduce((acc, v) => acc + v, 0),
);
document.getElementById("out3").textContent =
`values: ${JSON.stringify(result.values)}\nindex: ${JSON.stringify([result.index.at(0), result.index.at(1)])}`;
};

window.demo4 = () => {
const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [4, 5, 6] });
const result = applyDataFrame(
df,
(row) => row.values.reduce((acc, v) => acc + v, 0),
{ axis: 1 },
);
document.getElementById("out4").textContent = JSON.stringify(result.values);
};

window.demo5 = () => {
const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [4, 5, 6] });
const result = applyExpandDataFrame(df, (col) =>
new Series({ data: col.values.map((v) => v * 2), index: col.index }),
);
document.getElementById("out5").textContent =
`a: ${JSON.stringify(result.col("a").values)}\nb: ${JSON.stringify(result.col("b").values)}`;
};

window.demo6 = () => {
const df = DataFrame.fromColumns({ a: [1, 2, 3], b: [4, 5, 6] });
const result = mapDataFrame(df, (v) => v ** 2);
document.getElementById("out6").textContent =
`a: ${JSON.stringify(result.col("a").values)}\nb: ${JSON.stringify(result.col("b").values)}`;
};
</script>
</body>
</html>
Loading