Skip to content

Commit 952d479

Browse files
Iteration 151: Add 5 benchmark pairs (478 total, +5 vs best 473)
Added benchmarks: - df_any_all_axis1: anyDataFrame/allDataFrame row-wise (axis=1) - df_nunique_axis1: nuniqueDataFrame row-wise (axis=1) - cat_codes_accessor: CategoricalAccessor.codes/nCategories/ordered properties - ewm_adjust: EWM with adjust=false (IIR) vs adjust=true - interpolate_bfill_limit: interpolateSeries bfill method with limit option Run: https://github.com/githubnext/tsessebe/actions/runs/24562479978 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e4521f9 commit 952d479

10 files changed

Lines changed: 381 additions & 0 deletions
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
Benchmark: pd.Categorical.codes / categories / ordered — category accessor properties
3+
on a 100k-element categorical Series.
4+
Outputs JSON: {"function": "cat_codes_accessor", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
"""
6+
import json
7+
import time
8+
import numpy as np
9+
import pandas as pd
10+
11+
SIZE = 100_000
12+
CATS = 50
13+
WARMUP = 5
14+
ITERATIONS = 30
15+
16+
categories = [f"cat_{i}" for i in range(CATS)]
17+
data = [categories[i % CATS] for i in range(SIZE)]
18+
s = pd.Categorical(data, categories=categories)
19+
ps = pd.Series(s)
20+
21+
for _ in range(WARMUP):
22+
_ = ps.cat.codes
23+
_ = ps.cat.categories
24+
_ = ps.cat.ordered
25+
_ = len(ps.cat.categories)
26+
27+
times = []
28+
for _ in range(ITERATIONS):
29+
t0 = time.perf_counter()
30+
_ = ps.cat.codes
31+
_ = ps.cat.categories
32+
_ = ps.cat.ordered
33+
_ = len(ps.cat.categories)
34+
times.append((time.perf_counter() - t0) * 1000)
35+
36+
total_ms = sum(times)
37+
print(json.dumps({
38+
"function": "cat_codes_accessor",
39+
"mean_ms": round(total_ms / ITERATIONS, 3),
40+
"iterations": ITERATIONS,
41+
"total_ms": round(total_ms, 3),
42+
}))
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
Benchmark: DataFrame.any(axis=1) / all(axis=1) — row-wise boolean reductions on 100k-row DataFrame.
3+
Outputs JSON: {"function": "df_any_all_axis1", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import numpy as np
8+
import pandas as pd
9+
10+
SIZE = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 20
13+
14+
df = pd.DataFrame({
15+
"a": np.arange(SIZE) % 2 == 0,
16+
"b": np.arange(SIZE) % 3 != 0,
17+
"c": np.arange(SIZE) > 0,
18+
"d": np.arange(SIZE) % 5 == 0,
19+
})
20+
21+
for _ in range(WARMUP):
22+
df.any(axis=1)
23+
df.all(axis=1)
24+
25+
times = []
26+
for _ in range(ITERATIONS):
27+
t0 = time.perf_counter()
28+
df.any(axis=1)
29+
df.all(axis=1)
30+
times.append((time.perf_counter() - t0) * 1000)
31+
32+
total_ms = sum(times)
33+
print(json.dumps({
34+
"function": "df_any_all_axis1",
35+
"mean_ms": round(total_ms / ITERATIONS, 3),
36+
"iterations": ITERATIONS,
37+
"total_ms": round(total_ms, 3),
38+
}))
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""
2+
Benchmark: DataFrame.nunique(axis=1) — count unique values per row on a 10k-row DataFrame.
3+
Outputs JSON: {"function": "df_nunique_axis1", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import numpy as np
8+
import pandas as pd
9+
10+
SIZE = 10_000
11+
WARMUP = 5
12+
ITERATIONS = 30
13+
14+
df = pd.DataFrame({
15+
"a": np.arange(SIZE) % 5,
16+
"b": np.arange(SIZE) % 10,
17+
"c": np.arange(SIZE) % 3,
18+
"d": np.arange(SIZE) % 7,
19+
"e": np.arange(SIZE) % 4,
20+
})
21+
22+
for _ in range(WARMUP):
23+
df.nunique(axis=1)
24+
25+
times = []
26+
for _ in range(ITERATIONS):
27+
t0 = time.perf_counter()
28+
df.nunique(axis=1)
29+
times.append((time.perf_counter() - t0) * 1000)
30+
31+
total_ms = sum(times)
32+
print(json.dumps({
33+
"function": "df_nunique_axis1",
34+
"mean_ms": round(total_ms / ITERATIONS, 3),
35+
"iterations": ITERATIONS,
36+
"total_ms": round(total_ms, 3),
37+
}))
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Benchmark: EWM with adjust=False — IIR-based exponential weighted mean vs default adjust=True on 100k Series.
3+
Outputs JSON: {"function": "ewm_adjust", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import numpy as np
8+
import pandas as pd
9+
10+
SIZE = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 20
13+
14+
data = np.sin(np.arange(SIZE) * 0.01) * 100
15+
s = pd.Series(data)
16+
17+
for _ in range(WARMUP):
18+
s.ewm(alpha=0.3, adjust=False).mean()
19+
s.ewm(alpha=0.3, adjust=True).mean()
20+
s.ewm(span=20, adjust=False).mean()
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
s.ewm(alpha=0.3, adjust=False).mean()
26+
s.ewm(alpha=0.3, adjust=True).mean()
27+
s.ewm(span=20, adjust=False).mean()
28+
times.append((time.perf_counter() - t0) * 1000)
29+
30+
total_ms = sum(times)
31+
print(json.dumps({
32+
"function": "ewm_adjust",
33+
"mean_ms": round(total_ms / ITERATIONS, 3),
34+
"iterations": ITERATIONS,
35+
"total_ms": round(total_ms, 3),
36+
}))
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Benchmark: Series.interpolate with bfill method and limit option — backward fill with gap limit on 50k Series.
3+
Outputs JSON: {"function": "interpolate_bfill_limit", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import numpy as np
8+
import pandas as pd
9+
10+
SIZE = 50_000
11+
WARMUP = 5
12+
ITERATIONS = 30
13+
14+
data = np.where(np.arange(SIZE) % 7 < 2, np.nan, np.sin(np.arange(SIZE) * 0.01) * 100)
15+
s = pd.Series(data)
16+
17+
for _ in range(WARMUP):
18+
s.interpolate(method="bfill")
19+
s.ffill(limit=2)
20+
s.bfill(limit=1)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
s.interpolate(method="bfill")
26+
s.ffill(limit=2)
27+
s.bfill(limit=1)
28+
times.append((time.perf_counter() - t0) * 1000)
29+
30+
total_ms = sum(times)
31+
print(json.dumps({
32+
"function": "interpolate_bfill_limit",
33+
"mean_ms": round(total_ms / ITERATIONS, 3),
34+
"iterations": ITERATIONS,
35+
"total_ms": round(total_ms, 3),
36+
}))
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Benchmark: CategoricalAccessor.codes / nCategories / ordered — category accessor
3+
* properties on a 100k-element categorical Series.
4+
* Outputs JSON: {"function": "cat_codes_accessor", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
*/
6+
import { Series } from "../../src/index.ts";
7+
8+
const SIZE = 100_000;
9+
const CATS = 50;
10+
const WARMUP = 5;
11+
const ITERATIONS = 30;
12+
13+
const categories = Array.from({ length: CATS }, (_, i) => `cat_${i}`);
14+
const data = Array.from({ length: SIZE }, (_, i) => categories[i % CATS]);
15+
const s = new Series({ data });
16+
17+
for (let i = 0; i < WARMUP; i++) {
18+
void s.cat.codes;
19+
void s.cat.nCategories;
20+
void s.cat.ordered;
21+
void s.cat.categories;
22+
}
23+
24+
const start = performance.now();
25+
for (let i = 0; i < ITERATIONS; i++) {
26+
void s.cat.codes;
27+
void s.cat.nCategories;
28+
void s.cat.ordered;
29+
void s.cat.categories;
30+
}
31+
const total = performance.now() - start;
32+
33+
console.log(
34+
JSON.stringify({
35+
function: "cat_codes_accessor",
36+
mean_ms: total / ITERATIONS,
37+
iterations: ITERATIONS,
38+
total_ms: total,
39+
}),
40+
);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Benchmark: anyDataFrame / allDataFrame with axis=1 — row-wise boolean reductions on 100k-row DataFrame.
3+
* Outputs JSON: {"function": "df_any_all_axis1", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { DataFrame, Series, anyDataFrame, allDataFrame } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 20;
10+
11+
const df = new DataFrame({
12+
columns: new Map([
13+
["a", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 2 === 0) })],
14+
["b", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 3 !== 0) })],
15+
["c", new Series({ data: Array.from({ length: SIZE }, (_, i) => i > 0) })],
16+
["d", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 5 === 0) })],
17+
]),
18+
});
19+
20+
for (let i = 0; i < WARMUP; i++) {
21+
anyDataFrame(df, { axis: 1 });
22+
allDataFrame(df, { axis: 1 });
23+
}
24+
25+
const start = performance.now();
26+
for (let i = 0; i < ITERATIONS; i++) {
27+
anyDataFrame(df, { axis: 1 });
28+
allDataFrame(df, { axis: 1 });
29+
}
30+
const total = performance.now() - start;
31+
32+
console.log(
33+
JSON.stringify({
34+
function: "df_any_all_axis1",
35+
mean_ms: total / ITERATIONS,
36+
iterations: ITERATIONS,
37+
total_ms: total,
38+
}),
39+
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Benchmark: nuniqueDataFrame with axis=1 — count unique values per row on a 10k-row DataFrame.
3+
* Outputs JSON: {"function": "df_nunique_axis1", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { DataFrame, Series, nuniqueDataFrame } from "../../src/index.ts";
6+
7+
const SIZE = 10_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
const df = new DataFrame({
12+
columns: new Map([
13+
["a", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 5) })],
14+
["b", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 10) })],
15+
["c", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 3) })],
16+
["d", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 7) })],
17+
["e", new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 4) })],
18+
]),
19+
});
20+
21+
for (let i = 0; i < WARMUP; i++) {
22+
nuniqueDataFrame(df, { axis: 1 });
23+
}
24+
25+
const start = performance.now();
26+
for (let i = 0; i < ITERATIONS; i++) {
27+
nuniqueDataFrame(df, { axis: 1 });
28+
}
29+
const total = performance.now() - start;
30+
31+
console.log(
32+
JSON.stringify({
33+
function: "df_nunique_axis1",
34+
mean_ms: total / ITERATIONS,
35+
iterations: ITERATIONS,
36+
total_ms: total,
37+
}),
38+
);

benchmarks/tsb/bench_ewm_adjust.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Benchmark: EWM with adjust=false — IIR-based exponential weighted mean vs default adjust=true on 100k Series.
3+
* Outputs JSON: {"function": "ewm_adjust", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Series } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 20;
10+
11+
const data = Array.from({ length: SIZE }, (_, i) => Math.sin(i * 0.01) * 100);
12+
const s = new Series({ data });
13+
14+
for (let i = 0; i < WARMUP; i++) {
15+
s.ewm({ alpha: 0.3, adjust: false }).mean();
16+
s.ewm({ alpha: 0.3, adjust: true }).mean();
17+
s.ewm({ span: 20, adjust: false }).mean();
18+
}
19+
20+
const start = performance.now();
21+
for (let i = 0; i < ITERATIONS; i++) {
22+
s.ewm({ alpha: 0.3, adjust: false }).mean();
23+
s.ewm({ alpha: 0.3, adjust: true }).mean();
24+
s.ewm({ span: 20, adjust: false }).mean();
25+
}
26+
const total = performance.now() - start;
27+
28+
console.log(
29+
JSON.stringify({
30+
function: "ewm_adjust",
31+
mean_ms: total / ITERATIONS,
32+
iterations: ITERATIONS,
33+
total_ms: total,
34+
}),
35+
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Benchmark: interpolateSeries with bfill method and limit option — backward fill with gap limit on 50k Series.
3+
* Outputs JSON: {"function": "interpolate_bfill_limit", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Series, interpolateSeries } from "../../src/index.ts";
6+
7+
const SIZE = 50_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
// ~15% NaN values with consecutive gaps of up to 5
12+
const data = Array.from({ length: SIZE }, (_, i) => {
13+
const gap = i % 7;
14+
if (gap === 0 || gap === 1) return null;
15+
return Math.sin(i * 0.01) * 100;
16+
});
17+
const s = new Series({ data });
18+
19+
for (let i = 0; i < WARMUP; i++) {
20+
interpolateSeries(s, { method: "bfill" });
21+
interpolateSeries(s, { method: "ffill", limit: 2 });
22+
interpolateSeries(s, { method: "bfill", limit: 1 });
23+
}
24+
25+
const start = performance.now();
26+
for (let i = 0; i < ITERATIONS; i++) {
27+
interpolateSeries(s, { method: "bfill" });
28+
interpolateSeries(s, { method: "ffill", limit: 2 });
29+
interpolateSeries(s, { method: "bfill", limit: 1 });
30+
}
31+
const total = performance.now() - start;
32+
33+
console.log(
34+
JSON.stringify({
35+
function: "interpolate_bfill_limit",
36+
mean_ms: total / ITERATIONS,
37+
iterations: ITERATIONS,
38+
total_ms: total,
39+
}),
40+
);

0 commit comments

Comments
 (0)