Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f097310
Iteration 172: Add na_ops — isna/notna/ffill/bfill
github-actions[bot] Apr 11, 2026
ba88693
Iteration 174: Add pct_change for Series and DataFrame
github-actions[bot] Apr 11, 2026
e100b1c
Iteration 193: Add idxmin/idxmax for Series and DataFrame
github-actions[bot] Apr 11, 2026
7596cd3
Iteration 194: Add astype — dtype coercion for Series and DataFrame
github-actions[bot] Apr 11, 2026
4bb08d3
Iteration 195: Add replace — value substitution for Series and DataFrame
github-actions[bot] Apr 11, 2026
7aff7c0
Iteration 196: Add where/mask — conditional value selection for Serie…
github-actions[bot] Apr 11, 2026
8e37dcb
Iteration 197: Add diff/shift — discrete difference and value shiftin…
github-actions[bot] Apr 11, 2026
e216678
Iteration 199: Add duplicated/drop_duplicates and sample
github-actions[bot] Apr 11, 2026
705b036
Iteration 203: Add clip_advanced and apply/map
github-actions[bot] Apr 11, 2026
6ed6308
Iteration 204: Add cut/qcut — binning continuous data into intervals
github-actions[bot] Apr 11, 2026
79dac82
Iteration 205: Add Interval/IntervalIndex — pandas interval type and …
github-actions[bot] Apr 11, 2026
9637071
Iteration 206: Add getDummies/fromDummies — one-hot encoding
github-actions[bot] Apr 11, 2026
f77afa8
Iteration 208: Add crosstab — cross-tabulation of categorical factors
github-actions[bot] Apr 11, 2026
94eb1ed
Iteration 209: Add pivotTableFull — pivot_table with margins support
github-actions[bot] Apr 11, 2026
0aacdf5
Iteration 210: Add explode — DataFrame/Series list-cell expansion
github-actions[bot] Apr 11, 2026
3f46033
Iteration 212: Add factorize and wide_to_long
github-actions[bot] Apr 11, 2026
8f5fdc5
Iteration 213: Add interpolate — fill NaN via linear/pad/bfill/nearest
github-actions[bot] Apr 11, 2026
20706d7
Iteration 214: Add selectDtypes — filter DataFrame columns by dtype
github-actions[bot] Apr 11, 2026
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