Skip to content

Commit d195aa7

Browse files
authored
Merge remote-tracking branch 'origin/autoloop/build-tsb-pandas-typescript-migration-c9103f2f32e44258-08b7e7dc74ab9698' into copilot/merge-build-tsb-pandas-typescript-prs
Co-authored-by: mrjf <180956+mrjf@users.noreply.github.com>
2 parents b2e0933 + d87cb10 commit d195aa7

140 files changed

Lines changed: 41717 additions & 10 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

playground/add_sub_mul_div.html

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>tsb — add / sub / mul / div</title>
7+
<script src="playground-runtime.js"></script>
8+
<style>
9+
body { font-family: system-ui, sans-serif; max-width: 860px; margin: 2rem auto; padding: 0 1rem; color: #1a1a1a; }
10+
h1 { font-size: 2rem; margin-bottom: .25rem; }
11+
.subtitle { color: #555; margin-bottom: 2rem; }
12+
section { margin-bottom: 2.5rem; }
13+
h2 { font-size: 1.25rem; border-bottom: 2px solid #e0e0e0; padding-bottom: .35rem; margin-bottom: 1rem; }
14+
pre { background: #f5f5f5; border-radius: 6px; padding: 1rem; overflow-x: auto; font-size: .875rem; }
15+
code { font-family: "JetBrains Mono", "Fira Code", monospace; }
16+
.output { background: #eaf7ea; border-left: 4px solid #4caf50; padding: .75rem 1rem; border-radius: 0 6px 6px 0; margin-top: .5rem; font-size: .875rem; white-space: pre; font-family: monospace; }
17+
.note { background: #fff8e1; border-left: 4px solid #ffc107; padding: .6rem 1rem; border-radius: 0 6px 6px 0; font-size: .9rem; margin: .75rem 0; }
18+
nav { margin-bottom: 1.5rem; font-size: .9rem; color: #555; }
19+
nav a { color: #0366d6; text-decoration: none; }
20+
nav a:hover { text-decoration: underline; }
21+
</style>
22+
</head>
23+
<body>
24+
<nav><a href="index.html">← tsb playground</a></nav>
25+
<h1>add / sub / mul / div</h1>
26+
<p class="subtitle">
27+
Element-wise arithmetic between a Series (or DataFrame) and a scalar or another
28+
Series — mirrors <code>pandas.Series.add()</code>, <code>.sub()</code>,
29+
<code>.mul()</code>, and <code>.div()</code>.
30+
</p>
31+
32+
<section>
33+
<h2>1 — add: Series + scalar</h2>
34+
<p>
35+
<code>seriesAdd(series, scalar)</code> adds a constant to every element.
36+
Missing values (<code>null</code> / <code>NaN</code>) are propagated unchanged.
37+
Mirrors <code>pandas.Series.add(other)</code>.
38+
</p>
39+
<pre><code id="ex1-code">import { Series, seriesAdd } from "tsb";
40+
41+
const s = new Series({ data: [1, 2, null, 4] });
42+
const result = seriesAdd(s, 10);
43+
console.log([...result.values]); // [11, 12, null, 14]
44+
</code></pre>
45+
<div class="output" id="ex1-output">Loading…</div>
46+
</section>
47+
48+
<section>
49+
<h2>2 — add: Series + Series (positional)</h2>
50+
<p>
51+
When <code>other</code> is another Series, elements are paired positionally
52+
(same as pandas default when shapes match).
53+
</p>
54+
<pre><code id="ex2-code">import { Series, seriesAdd } from "tsb";
55+
56+
const a = new Series({ data: [1, 2, 3] });
57+
const b = new Series({ data: [4, 5, 6] });
58+
seriesAdd(a, b).values; // [5, 7, 9]
59+
</code></pre>
60+
<div class="output" id="ex2-output">Loading…</div>
61+
</section>
62+
63+
<section>
64+
<h2>3 — sub / rsub</h2>
65+
<p>
66+
<code>seriesSub(s, other)</code> computes <code>s − other</code>.
67+
<code>seriesRsub(s, other)</code> computes the reverse: <code>other − s</code>.
68+
</p>
69+
<pre><code id="ex3-code">import { Series, seriesSub, seriesRsub } from "tsb";
70+
71+
const s = new Series({ data: [10, 20, 30] });
72+
seriesSub(s, 5).values; // [5, 15, 25]
73+
seriesRsub(s, 100).values; // [90, 80, 70]
74+
</code></pre>
75+
<div class="output" id="ex3-output">Loading…</div>
76+
</section>
77+
78+
<section>
79+
<h2>4 — mul: multiply</h2>
80+
<p>
81+
<code>seriesMul(s, other)</code> multiplies every element.
82+
<code>seriesRmul</code> is the reversed form (commutative, provided for API symmetry).
83+
</p>
84+
<pre><code id="ex4-code">import { Series, seriesMul } from "tsb";
85+
86+
const s = new Series({ data: [1, 2, 3, null] });
87+
seriesMul(s, 3).values; // [3, 6, 9, null]
88+
89+
const weights = new Series({ data: [0.5, 1, 2, 1] });
90+
seriesMul(s, weights).values; // [0.5, 2, 6, null]
91+
</code></pre>
92+
<div class="output" id="ex4-output">Loading…</div>
93+
</section>
94+
95+
<section>
96+
<h2>5 — div / rdiv (true division)</h2>
97+
<p>
98+
<code>seriesDiv(s, other)</code> performs IEEE-754 true division.
99+
Division by zero yields <code>±Infinity</code> or <code>NaN</code> (0÷0),
100+
matching <code>pandas.Series.div</code>.
101+
<code>seriesRdiv(s, other)</code> computes <code>other / s</code>.
102+
</p>
103+
<pre><code id="ex5-code">import { Series, seriesDiv, seriesRdiv } from "tsb";
104+
105+
const s = new Series({ data: [4, 9, 0, null] });
106+
seriesDiv(s, 2).values; // [2, 4.5, Infinity, null]
107+
seriesRdiv(s, 36).values; // [9, 4, Infinity, null]
108+
</code></pre>
109+
<div class="output" id="ex5-output">Loading…</div>
110+
</section>
111+
112+
<section>
113+
<h2>6 — DataFrame arithmetic</h2>
114+
<p>
115+
All four operations work on DataFrames too. A scalar is broadcast across
116+
every cell; a DataFrame operand is paired column-by-column, row-by-row.
117+
</p>
118+
<pre><code id="ex6-code">import { DataFrame, dataFrameAdd, dataFrameMul, dataFrameDiv } from "tsb";
119+
120+
const df = DataFrame.fromColumns({ price: [10, 20, 30], qty: [3, 5, 2] });
121+
122+
// Add a discount
123+
dataFrameAdd(df, 5).col("price").values; // [15, 25, 35]
124+
125+
// Scale everything by 2
126+
dataFrameMul(df, 2).col("qty").values; // [6, 10, 4]
127+
128+
// Revenue per item / some constant
129+
dataFrameDiv(df, 10).col("price").values; // [1, 2, 3]
130+
</code></pre>
131+
<div class="output" id="ex6-output">Loading…</div>
132+
</section>
133+
134+
<section>
135+
<h2>7 — Missing value propagation</h2>
136+
<p>
137+
Following pandas convention, any operation involving a missing value
138+
(<code>null</code> or <code>NaN</code>) returns the missing value unchanged.
139+
</p>
140+
<pre><code id="ex7-code">import { Series, seriesAdd, seriesMul, seriesDiv } from "tsb";
141+
142+
const s = new Series({ data: [1, null, NaN, 4] });
143+
seriesAdd(s, 10).values; // [11, null, NaN, 14]
144+
seriesMul(s, 2).values; // [2, null, NaN, 8]
145+
seriesDiv(s, 2).values; // [0.5, null, NaN, 2]
146+
</code></pre>
147+
<div class="output" id="ex7-output">Loading…</div>
148+
</section>
149+
150+
<script>
151+
// Simple synchronous demos (no module bundler needed for the playground)
152+
const demos = [
153+
// 1
154+
() => {
155+
const values = [1, 2, null, 4];
156+
const result = values.map(v => v === null ? null : v + 10);
157+
return `[${result.map(v => v === null ? 'null' : v).join(', ')}]`;
158+
},
159+
// 2
160+
() => {
161+
const a = [1, 2, 3], b = [4, 5, 6];
162+
const r = a.map((v, i) => v + b[i]);
163+
return `[${r.join(', ')}]`;
164+
},
165+
// 3
166+
() => {
167+
const data = [10, 20, 30];
168+
const sub = data.map(v => v - 5).join(', ');
169+
const rsub = data.map(v => 100 - v).join(', ');
170+
return `sub: [${sub}]\nrsub: [${rsub}]`;
171+
},
172+
// 4
173+
() => {
174+
const data = [1, 2, 3, null];
175+
const mul = data.map(v => v === null ? null : v * 3);
176+
const weights = [0.5, 1, 2, 1];
177+
const mulW = data.map((v, i) => v === null ? null : v * weights[i]);
178+
return `scalar: [${mul.map(v => v ?? 'null').join(', ')}]\nvector: [${mulW.map(v => v ?? 'null').join(', ')}]`;
179+
},
180+
// 5
181+
() => {
182+
const data = [4, 9, 0, null];
183+
const div = data.map(v => v === null ? null : v / 2);
184+
const rdiv = data.map(v => v === null ? null : 36 / v);
185+
return `div: [${div.map(v => v ?? 'null').join(', ')}]\nrdiv: [${rdiv.map(v => v ?? 'null').join(', ')}]`;
186+
},
187+
// 6
188+
() => {
189+
const price = [10, 20, 30], qty = [3, 5, 2];
190+
const addP = price.map(v => v + 5).join(', ');
191+
const mulQ = qty.map(v => v * 2).join(', ');
192+
const divP = price.map(v => v / 10).join(', ');
193+
return `add price: [${addP}]\nmul qty: [${mulQ}]\ndiv price: [${divP}]`;
194+
},
195+
// 7
196+
() => {
197+
const data = [1, null, NaN, 4];
198+
const add = data.map(v => v === null || (typeof v === 'number' && Number.isNaN(v)) ? v : v + 10);
199+
const mul = data.map(v => v === null || (typeof v === 'number' && Number.isNaN(v)) ? v : v * 2);
200+
const div = data.map(v => v === null || (typeof v === 'number' && Number.isNaN(v)) ? v : v / 2);
201+
const fmt = arr => arr.map(v => v === null ? 'null' : (typeof v === 'number' && Number.isNaN(v) ? 'NaN' : v)).join(', ');
202+
return `add: [${fmt(add)}]\nmul: [${fmt(mul)}]\ndiv: [${fmt(div)}]`;
203+
},
204+
];
205+
206+
demos.forEach((fn, i) => {
207+
const el = document.getElementById(`ex${i+1}-output`);
208+
if (el) {
209+
try { el.textContent = fn(); } catch(e) { el.textContent = `Error: ${e.message}`; }
210+
}
211+
});
212+
</script>
213+
</body>
214+
</html>

0 commit comments

Comments
 (0)