Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
532569e
Iteration 231: Add timedelta_range
github-actions[bot] Apr 21, 2026
65fb352
Iteration 232: Consolidate Timedelta classes, fix timedelta_range str…
github-actions[bot] Apr 21, 2026
ae530a9
Iteration 233: Add queryDataFrame and evalDataFrame (DataFrame.query …
github-actions[bot] Apr 21, 2026
d5a1d27
feat(core): add DataFrame.fromArrays as alias for fromColumns
github-actions[bot] Apr 21, 2026
f3af87d
fix(eval_query): stabilize eval zero behavior and property test
Copilot Apr 21, 2026
75fd919
Iteration 235: Add strFindall/strFindallCount/strFindFirst/strFindall…
github-actions[bot] Apr 21, 2026
e1cf834
fix(lint): add missing return types and fix import restrictions
github-actions[bot] Apr 21, 2026
fed12ad
Iteration 237: Add cutBinsToFrame + xs cross-section selection
github-actions[bot] Apr 22, 2026
9bfae87
Iteration 239: Fix CI type errors + add swapLevelSeries/DataFrame + t…
github-actions[bot] Apr 22, 2026
3fabb3e
Iteration 240: Add seriesBetween, seriesUpdate/dataFrameUpdate, filte…
github-actions[bot] Apr 22, 2026
3f99b93
Iteration 241: Add combineSeries/combineDataFrame + keepTrue/keepFals…
github-actions[bot] Apr 22, 2026
5c00190
Iteration 242: Add scalar_extract + corrwith/autoCorr
github-actions[bot] Apr 22, 2026
dedd132
Iteration 243: +rename_ops (renameSeriesIndex/DataFrame, addPrefix/Su…
github-actions[bot] Apr 22, 2026
61ed13e
Iteration 244: +dot_matmul (seriesDotSeries/DataFrame/dataFrameDotSer…
github-actions[bot] Apr 22, 2026
e9c12f1
Iteration 245: +at_iat (seriesAt/seriesIat/dataFrameAt/dataFrameIat) …
github-actions[bot] Apr 22, 2026
88bdff9
Iteration 247: +join (join/joinAll/crossJoin) +infer_objects (inferOb…
github-actions[bot] Apr 22, 2026
9076ac4
Iteration 248: +merge_asof (ordered nearest-key left-join)
github-actions[bot] Apr 22, 2026
77412da
fix: resolve TypeScript typecheck errors from iteration 248
github-actions[bot] Apr 22, 2026
f0efc42
Iteration 249: +mergeOrdered (ordered fill merge)
github-actions[bot] Apr 22, 2026
45a0c0f
fix: resolve all biome lint errors (noParameterProperties, noParamete…
github-actions[bot] Apr 22, 2026
1e590c2
Iteration 251: add resample() time-based resampling for Series/DataFrame
github-actions[bot] Apr 22, 2026
8d2e375
fix: add Date to Label type for datetime index support
github-actions[bot] Apr 22, 2026
a7d40f4
Iteration 258: add pd.testing (assertSeriesEqual/assertFrameEqual/ass…
github-actions[bot] Apr 23, 2026
02a6628
fix(typecheck): handle Date labels and non-null reductions
Copilot Apr 23, 2026
cb651c2
fix(multi_index): use deterministic mixed-type label ordering
Copilot Apr 23, 2026
699d4ae
fix(ci): resolve lint and formatting errors
github-actions[bot] Apr 23, 2026
66f35cd
fix: resolve CI test failures across join, index handling, stats, and…
Copilot Apr 23, 2026
77a3185
fix: address review findings in merge/xs/str_findall/multi_index
Copilot Apr 23, 2026
b5bdc63
chore: apply final review polish for testing and regex helpers
Copilot Apr 23, 2026
9746c2e
fix(lint): apply biome formatter fix to corrwith.test.ts
github-actions[bot] Apr 23, 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
88 changes: 88 additions & 0 deletions playground/at_iat.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>at_iat — tsb playground</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 860px; margin: 2rem auto; padding: 0 1rem; }
h1 { font-size: 1.6rem; }
h2 { font-size: 1.2rem; margin-top: 2rem; }
pre { background: #f6f8fa; border-radius: 6px; padding: 1rem; overflow-x: auto; }
code { font-family: monospace; }
.label { color: #555; font-size: 0.85rem; }
.back { display: inline-block; margin-bottom: 1rem; color: #0969da; text-decoration: none; }
.back:hover { text-decoration: underline; }
</style>
</head>
<body>
<a class="back" href="index.html">← Back to playground index</a>
<h1>at_iat — fast scalar access for Series and DataFrame</h1>
<p>
Fast single-cell accessors that mirror the pandas <code>.at</code> and <code>.iat</code>
indexers. Use these when you need a single scalar value — they are clearer and faster
than <code>.loc</code> / <code>.iloc</code> for single-element access.
</p>

<h2>seriesAt — access by label</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>import pandas as pd
s = pd.Series([10, 20, 30], index=["a", "b", "c"])
s.at["b"] # 20
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { Series, seriesAt } from "tsb";

const s = new Series({ data: [10, 20, 30], index: ["a", "b", "c"] });
seriesAt(s, "b"); // 20
seriesAt(s, "a"); // 10
</code></pre>

<h2>seriesIat — access by integer position</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>s.iat[2] # 30
s.iat[-1] # 30 (negative indexing)
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { seriesIat } from "tsb";

seriesIat(s, 2); // 30
seriesIat(s, -1); // 30
</code></pre>

<h2>dataFrameAt — access by row label and column name</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>df = pd.DataFrame({"x": [1, 2], "y": [3, 4]}, index=["r0", "r1"])
df.at["r1", "x"] # 2
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { DataFrame, dataFrameAt } from "tsb";

const df = DataFrame.fromColumns(
{ x: [1, 2], y: [3, 4] },
{ index: ["r0", "r1"] },
);
dataFrameAt(df, "r1", "x"); // 2
dataFrameAt(df, "r0", "y"); // 3
</code></pre>

<h2>dataFrameIat — access by integer row and column position</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>df.iat[0, 1] # 3 (row 0, col 1 = "y")
df.iat[1, -1] # 4 (last col)
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { dataFrameIat } from "tsb";

dataFrameIat(df, 0, 1); // 3 (row 0, column index 1 = "y")
dataFrameIat(df, 1, -1); // 4 (last column, row 1)
</code></pre>

<h2>Summary</h2>
<pre><code>// seriesAt(s, label) — label-based scalar access
// seriesIat(s, i) — position-based scalar access
// dataFrameAt(df, rowLabel, col) — label × label scalar access
// dataFrameIat(df, rowInt, col) — position × position scalar access
</code></pre>
</body>
</html>
110 changes: 110 additions & 0 deletions playground/between.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>between — tsb playground</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 860px; margin: 2rem auto; padding: 0 1rem; }
h1 { font-size: 1.6rem; }
h2 { font-size: 1.2rem; margin-top: 2rem; }
pre { background: #f6f8fa; border-radius: 6px; padding: 1rem; overflow-x: auto; }
code { font-family: monospace; }
.label { color: #555; font-size: 0.85rem; }
.back { display: inline-block; margin-bottom: 1rem; color: #0969da; text-decoration: none; }
.back:hover { text-decoration: underline; }
</style>
</head>
<body>
<a class="back" href="index.html">← Back to playground index</a>
<h1>between</h1>
<p>
Element-wise range check: returns a boolean Series indicating whether each value lies
within <code>[left, right]</code>. Mirrors <code>pandas.Series.between</code>.
</p>

<h2>seriesBetween — inclusive="both" (default)</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>import pandas as pd

s = pd.Series([1, 2, 3, 4, 5])
print(s.between(2, 4))
# 0 False
# 1 True
# 2 True
# 3 True
# 4 False
# dtype: bool
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { Series, seriesBetween } from "tsb";

const s = new Series({ data: [1, 2, 3, 4, 5] });
seriesBetween(s, 2, 4).values;
// [false, true, true, true, false]
</code></pre>

<h2>Inclusive options</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>import pandas as pd

s = pd.Series([1, 2, 3, 4, 5])

s.between(2, 4, inclusive="left").tolist()
# [False, True, True, False, False]

s.between(2, 4, inclusive="right").tolist()
# [False, False, True, True, False]

s.between(2, 4, inclusive="neither").tolist()
# [False, False, True, False, False]
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { Series, seriesBetween } from "tsb";

const s = new Series({ data: [1, 2, 3, 4, 5] });

seriesBetween(s, 2, 4, { inclusive: "left" }).values;
// [false, true, true, false, false]

seriesBetween(s, 2, 4, { inclusive: "right" }).values;
// [false, false, true, true, false]

seriesBetween(s, 2, 4, { inclusive: "neither" }).values;
// [false, false, true, false, false]
</code></pre>

<h2>Missing values</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>import pandas as pd
import numpy as np

s = pd.Series([1, None, np.nan, 4])
s.between(0, 5).tolist()
# [True, False, False, True]
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { Series, seriesBetween } from "tsb";

const s = new Series({ data: [1, null, NaN, 4] });
seriesBetween(s, 0, 5).values;
// [true, false, false, true]
</code></pre>

<h2>String comparison</h2>
<p class="label">Python pandas equivalent:</p>
<pre><code>import pandas as pd

s = pd.Series(["apple", "banana", "cherry", "date"])
s.between("banana", "cherry").tolist()
# [False, True, True, False]
</code></pre>
<p class="label">tsb equivalent:</p>
<pre><code>import { Series, seriesBetween } from "tsb";

const s = new Series({ data: ["apple", "banana", "cherry", "date"] });
seriesBetween(s, "banana", "cherry").values;
// [false, true, true, false]
</code></pre>
</body>
</html>
109 changes: 109 additions & 0 deletions playground/combine.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>combine — Element-wise Combination — tsb playground</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 900px; margin: 2rem auto; padding: 0 1rem; }
h1 { color: #1a56db; }
pre { background: #f4f4f4; padding: 1rem; border-radius: 6px; overflow-x: auto; }
.output { background: #e8f5e9; padding: 1rem; border-radius: 6px; white-space: pre; font-family: monospace; min-height: 4rem; }
button { background: #1a56db; color: white; border: none; padding: 0.5rem 1.2rem; border-radius: 4px; cursor: pointer; margin: 0.2rem; }
button:hover { background: #1345b3; }
</style>
</head>
<body>
<h1>combine — Element-wise Combination</h1>
<p>
<code>combineSeries(a, b, func)</code> and <code>combineDataFrame(a, b, func)</code>
combine two objects element-wise using a caller-supplied binary function.
The result index is the <strong>union</strong> of both indices; a
<code>fillValue</code> (default <code>null</code>) is used when only one
side has a value for a given label.
</p>

<h2>Interactive Demo</h2>
<button onclick="runSeriesMax()">Series: max</button>
<button onclick="runSeriesUnion()">Series: union index</button>
<button onclick="runDfShared()">DataFrame: shared columns</button>
<button onclick="runDfUnion()">DataFrame: union columns</button>
<div class="output" id="output">Click a button above to run an example.</div>

<h2>Code Examples</h2>
<pre><code>import { Series, DataFrame, combineSeries, combineDataFrame } from "tsb";

// ── Series ──────────────────────────────────────────────────────────────────
const a = new Series({ data: [1, 5, 3], index: [0, 1, 2] });
const b = new Series({ data: [10, 2, 30], index: [0, 1, 2] });

// Element-wise max
combineSeries(a, b, (x, y) => Math.max(x, y)).values; // [10, 5, 30]

// Union index with fillValue=0
const c = new Series({ data: [1, 2], index: ["x", "y"] });
const d = new Series({ data: [10, 30], index: ["x", "z"] });
combineSeries(c, d, (x, y) => (x ?? 0) + (y ?? 0), 0).values;
// x:11, y:2, z:30

// ── DataFrame ───────────────────────────────────────────────────────────────
const df1 = DataFrame.fromColumns({ a: [1, 5], b: [100, 200] });
const df2 = DataFrame.fromColumns({ a: [10, 2], c: [1000, 2000] });

// Shared column "a": element-wise min; unshared columns processed with fillValue
combineDataFrame(df1, df2, (p, q) => Math.min(p ?? Infinity, q ?? Infinity));

// overwrite: false — unshared columns preserved as-is
combineDataFrame(df1, df2, (p, q) => Math.min(p ?? Infinity, q ?? Infinity),
{ overwrite: false });
</code></pre>

<script type="module">
const mod = await import("../src/index.ts").catch(() => null);

function display(label, text) {
document.getElementById("output").textContent = label + "\n\n" + text;
}

window.runSeriesMax = function () {
if (!mod) { display("", "(Module unavailable — run via bun)"); return; }
const { Series, combineSeries } = mod;
const a = new Series({ data: [1, 5, 3], index: [0, 1, 2] });
const b = new Series({ data: [10, 2, 30], index: [0, 1, 2] });
const result = combineSeries(a, b, (x, y) => Math.max(x, y));
display("combineSeries(a, b, Math.max):", String(result));
};

window.runSeriesUnion = function () {
if (!mod) { display("", "(Module unavailable — run via bun)"); return; }
const { Series, combineSeries } = mod;
const c = new Series({ data: [1, 2], index: ["x", "y"] });
const d = new Series({ data: [10, 30], index: ["x", "z"] });
const result = combineSeries(c, d, (x, y) => (x ?? 0) + (y ?? 0), 0);
display("combineSeries with union index (fillValue=0):", String(result));
};

window.runDfShared = function () {
if (!mod) { display("", "(Module unavailable — run via bun)"); return; }
const { DataFrame, combineDataFrame } = mod;
const df1 = DataFrame.fromColumns({ a: [1, 5, 3], b: [100, 200, 300] });
const df2 = DataFrame.fromColumns({ a: [10, 2, 30], b: [50, 250, 150] });
const result = combineDataFrame(df1, df2, (p, q) => Math.max(p, q));
display("combineDataFrame(df1, df2, Math.max) — shared columns:", String(result));
};

window.runDfUnion = function () {
if (!mod) { display("", "(Module unavailable — run via bun)"); return; }
const { DataFrame, combineDataFrame } = mod;
const df1 = DataFrame.fromColumns({ a: [1, 2], b: [10, 20] });
const df2 = DataFrame.fromColumns({ a: [100, 200], c: [1000, 2000] });
const result = combineDataFrame(df1, df2, (p, q) => {
if (p === null) return q;
if (q === null) return p;
return Math.min(p, q);
}, { overwrite: false });
display("combineDataFrame — union columns, overwrite=false:", String(result));
};
</script>
</body>
</html>
Loading
Loading