Skip to content

Commit d9b3dbf

Browse files
Iteration 187: 5 new benchmark pairs (539 total, +5 vs canonical 534)
Add benchmarks for Hour/Second DateOffset, digitize standalone, toNumeric generic, NamedAgg class/factory/isNamedAggSpec, and combineFirstSeries standalone. These cover previously unbenchmarked exported symbols from src/. Run: https://github.com/githubnext/tsessebe/actions/runs/24603938988 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent be69e38 commit d9b3dbf

10 files changed

Lines changed: 387 additions & 0 deletions
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Benchmark: Series.combine_first (standalone equivalent) — fill missing values from another Series.
2+
Mirrors tsb bench_combine_first_series.ts for pandas.
3+
"""
4+
import json, time
5+
import pandas as pd
6+
import numpy as np
7+
8+
SIZE = 10_000
9+
WARMUP = 5
10+
ITERATIONS = 50
11+
12+
data1 = [None if i % 3 == 0 else i * 0.5 for i in range(SIZE)]
13+
data2 = [i * 0.1 for i in range(SIZE)]
14+
s1 = pd.Series(data1)
15+
s2 = pd.Series(data2)
16+
17+
for _ in range(WARMUP):
18+
s1.combine_first(s2)
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
s1.combine_first(s2)
24+
times.append((time.perf_counter() - t0) * 1000)
25+
26+
total = sum(times)
27+
mean = total / ITERATIONS
28+
print(json.dumps({
29+
"function": "combine_first_series",
30+
"mean_ms": round(mean, 3),
31+
"iterations": ITERATIONS,
32+
"total_ms": round(total, 3),
33+
}))
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Benchmark: DateOffset Hour and Second — apply operations on 5k dates.
2+
Mirrors tsb bench_date_offset_hour_second.ts for pandas.
3+
"""
4+
import json, time
5+
from datetime import timedelta
6+
import pandas as pd
7+
from pandas.tseries.offsets import Hour, Second
8+
9+
SIZE = 5_000
10+
WARMUP = 5
11+
ITERATIONS = 50
12+
13+
hour = Hour(3)
14+
second = Second(90)
15+
base = pd.Timestamp("2020-01-15 10:00:00", tz="UTC")
16+
dates = [base + timedelta(minutes=i) for i in range(SIZE)]
17+
18+
for _ in range(WARMUP):
19+
for d in dates[:100]:
20+
d + hour
21+
d + second
22+
23+
times = []
24+
for _ in range(ITERATIONS):
25+
t0 = time.perf_counter()
26+
for d in dates:
27+
d + hour
28+
d + second
29+
times.append((time.perf_counter() - t0) * 1000)
30+
31+
total = sum(times)
32+
mean = total / ITERATIONS
33+
print(json.dumps({
34+
"function": "date_offset_hour_second",
35+
"mean_ms": round(mean, 3),
36+
"iterations": ITERATIONS,
37+
"total_ms": round(total, 3),
38+
}))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Benchmark: numpy.digitize (standalone) — bin 50k values into 10 bins.
2+
Mirrors tsb bench_digitize_fn.ts for numpy/pandas.
3+
"""
4+
import json, time
5+
import numpy as np
6+
7+
SIZE = 50_000
8+
WARMUP = 5
9+
ITERATIONS = 50
10+
11+
rng = np.random.default_rng(42)
12+
values = np.where(
13+
np.arange(SIZE) % 20 == 0,
14+
np.nan,
15+
(np.arange(SIZE) % 100) * 0.1,
16+
).tolist()
17+
bins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
18+
19+
for _ in range(WARMUP):
20+
np.digitize(values, bins)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
np.digitize(values, bins)
26+
times.append((time.perf_counter() - t0) * 1000)
27+
28+
total = sum(times)
29+
mean = total / ITERATIONS
30+
print(json.dumps({
31+
"function": "digitize_fn",
32+
"mean_ms": round(mean, 3),
33+
"iterations": ITERATIONS,
34+
"total_ms": round(total, 3),
35+
}))
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Benchmark: pd.NamedAgg class construction and isinstance validation — 100 specs × 1000 iters.
2+
Mirrors tsb bench_named_agg_class.ts for pandas.
3+
"""
4+
import json, time
5+
import pandas as pd
6+
7+
WARMUP = 5
8+
ITERATIONS = 1_000
9+
N = 100
10+
11+
sample_spec = {
12+
"total": pd.NamedAgg(column="salary", aggfunc="sum"),
13+
"avg": pd.NamedAgg(column="salary", aggfunc="mean"),
14+
"max": pd.NamedAgg(column="salary", aggfunc="max"),
15+
"cnt": pd.NamedAgg(column="headcount", aggfunc="count"),
16+
}
17+
18+
def is_named_agg_spec(spec):
19+
return isinstance(spec, dict) and all(isinstance(v, pd.NamedAgg) for v in spec.values())
20+
21+
for _ in range(WARMUP):
22+
for _ in range(N):
23+
pd.NamedAgg(column="salary", aggfunc="sum")
24+
pd.NamedAgg(column="score", aggfunc="mean")
25+
is_named_agg_spec(sample_spec)
26+
is_named_agg_spec({"x": "not-namedagg"})
27+
28+
times = []
29+
for _ in range(ITERATIONS):
30+
t0 = time.perf_counter()
31+
for _ in range(N):
32+
pd.NamedAgg(column="salary", aggfunc="sum")
33+
pd.NamedAgg(column="score", aggfunc="mean")
34+
is_named_agg_spec(sample_spec)
35+
is_named_agg_spec({"x": "not-namedagg"})
36+
times.append((time.perf_counter() - t0) * 1000)
37+
38+
total = sum(times)
39+
mean = total / ITERATIONS
40+
print(json.dumps({
41+
"function": "named_agg_class",
42+
"mean_ms": round(mean, 3),
43+
"iterations": ITERATIONS,
44+
"total_ms": round(total, 3),
45+
}))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Benchmark: pd.to_numeric generic dispatcher — coerce scalars, lists, and Series.
2+
Mirrors tsb bench_to_numeric_generic.ts for pandas.
3+
"""
4+
import json, time
5+
import pandas as pd
6+
7+
SIZE = 10_000
8+
WARMUP = 5
9+
ITERATIONS = 50
10+
11+
str_nums = [str(i * 0.1) for i in range(SIZE)]
12+
series = pd.Series(str_nums)
13+
14+
for _ in range(WARMUP):
15+
pd.to_numeric("3.14")
16+
pd.to_numeric(str_nums[:100], errors="coerce")
17+
pd.to_numeric(series, errors="coerce")
18+
19+
times = []
20+
for _ in range(ITERATIONS):
21+
t0 = time.perf_counter()
22+
pd.to_numeric("3.14")
23+
pd.to_numeric(str_nums, errors="coerce")
24+
pd.to_numeric(series, errors="coerce")
25+
times.append((time.perf_counter() - t0) * 1000)
26+
27+
total = sum(times)
28+
mean = total / ITERATIONS
29+
print(json.dumps({
30+
"function": "to_numeric_generic",
31+
"mean_ms": round(mean, 3),
32+
"iterations": ITERATIONS,
33+
"total_ms": round(total, 3),
34+
}))
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Benchmark: combineFirstSeries (standalone) — fill missing values from another Series.
3+
* Outputs JSON: {"function": "combine_first_series", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { combineFirstSeries, Series } from "../../src/index.ts";
6+
7+
const SIZE = 10_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const data1: (number | null)[] = Array.from({ length: SIZE }, (_, i) =>
12+
i % 3 === 0 ? null : i * 0.5,
13+
);
14+
const data2 = Array.from({ length: SIZE }, (_, i) => i * 0.1);
15+
const s1 = new Series({ data: data1 });
16+
const s2 = new Series({ data: data2 });
17+
18+
for (let i = 0; i < WARMUP; i++) {
19+
combineFirstSeries(s1, s2);
20+
}
21+
22+
const times: number[] = [];
23+
for (let i = 0; i < ITERATIONS; i++) {
24+
const start = performance.now();
25+
combineFirstSeries(s1, s2);
26+
times.push(performance.now() - start);
27+
}
28+
29+
const totalMs = times.reduce((a, b) => a + b, 0);
30+
const meanMs = totalMs / ITERATIONS;
31+
console.log(
32+
JSON.stringify({
33+
function: "combine_first_series",
34+
mean_ms: Math.round(meanMs * 1000) / 1000,
35+
iterations: ITERATIONS,
36+
total_ms: Math.round(totalMs * 1000) / 1000,
37+
}),
38+
);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Benchmark: DateOffset Hour and Second — apply operations on 5k dates.
3+
* Outputs JSON: {"function": "date_offset_hour_second", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Hour, Second } from "../../src/index.ts";
6+
7+
const SIZE = 5_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const hour = new Hour(3);
12+
const second = new Second(90);
13+
const base = new Date(Date.UTC(2020, 0, 15, 10, 0, 0));
14+
const dates = Array.from({ length: SIZE }, (_, i) => new Date(base.getTime() + i * 60_000));
15+
16+
for (let i = 0; i < WARMUP; i++) {
17+
for (const d of dates.slice(0, 100)) {
18+
hour.apply(d);
19+
second.apply(d);
20+
}
21+
}
22+
23+
const times: number[] = [];
24+
for (let i = 0; i < ITERATIONS; i++) {
25+
const start = performance.now();
26+
for (const d of dates) {
27+
hour.apply(d);
28+
second.apply(d);
29+
}
30+
times.push(performance.now() - start);
31+
}
32+
33+
const totalMs = times.reduce((a, b) => a + b, 0);
34+
const meanMs = totalMs / ITERATIONS;
35+
console.log(
36+
JSON.stringify({
37+
function: "date_offset_hour_second",
38+
mean_ms: Math.round(meanMs * 1000) / 1000,
39+
iterations: ITERATIONS,
40+
total_ms: Math.round(totalMs * 1000) / 1000,
41+
}),
42+
);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Benchmark: digitize (standalone) — bin 50k values into 10 bins.
3+
* Outputs JSON: {"function": "digitize_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { digitize } from "../../src/index.ts";
6+
7+
const SIZE = 50_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const values: (number | null)[] = Array.from({ length: SIZE }, (_, i) =>
12+
i % 20 === 0 ? null : (i % 100) * 0.1,
13+
);
14+
const bins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
15+
16+
for (let i = 0; i < WARMUP; i++) {
17+
digitize(values, bins);
18+
}
19+
20+
const times: number[] = [];
21+
for (let i = 0; i < ITERATIONS; i++) {
22+
const start = performance.now();
23+
digitize(values, bins);
24+
times.push(performance.now() - start);
25+
}
26+
27+
const totalMs = times.reduce((a, b) => a + b, 0);
28+
const meanMs = totalMs / ITERATIONS;
29+
console.log(
30+
JSON.stringify({
31+
function: "digitize_fn",
32+
mean_ms: Math.round(meanMs * 1000) / 1000,
33+
iterations: ITERATIONS,
34+
total_ms: Math.round(totalMs * 1000) / 1000,
35+
}),
36+
);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Benchmark: NamedAgg class, namedAgg factory, isNamedAggSpec — construct and validate 10k specs.
3+
* Outputs JSON: {"function": "named_agg_class", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { NamedAgg, namedAgg, isNamedAggSpec } from "../../src/index.ts";
6+
7+
const WARMUP = 5;
8+
const ITERATIONS = 1_000;
9+
const N = 100;
10+
11+
const sampleSpec = {
12+
total: namedAgg("salary", "sum"),
13+
avg: namedAgg("salary", "mean"),
14+
max: namedAgg("salary", "max"),
15+
cnt: namedAgg("headcount", "count"),
16+
};
17+
18+
for (let i = 0; i < WARMUP; i++) {
19+
for (let j = 0; j < N; j++) {
20+
new NamedAgg("salary", "sum");
21+
namedAgg("score", "mean");
22+
isNamedAggSpec(sampleSpec);
23+
isNamedAggSpec({ x: "not-namedagg" });
24+
}
25+
}
26+
27+
const times: number[] = [];
28+
for (let i = 0; i < ITERATIONS; i++) {
29+
const start = performance.now();
30+
for (let j = 0; j < N; j++) {
31+
new NamedAgg("salary", "sum");
32+
namedAgg("score", "mean");
33+
isNamedAggSpec(sampleSpec);
34+
isNamedAggSpec({ x: "not-namedagg" });
35+
}
36+
times.push(performance.now() - start);
37+
}
38+
39+
const totalMs = times.reduce((a, b) => a + b, 0);
40+
const meanMs = totalMs / ITERATIONS;
41+
console.log(
42+
JSON.stringify({
43+
function: "named_agg_class",
44+
mean_ms: Math.round(meanMs * 1000) / 1000,
45+
iterations: ITERATIONS,
46+
total_ms: Math.round(totalMs * 1000) / 1000,
47+
}),
48+
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Benchmark: toNumeric (generic dispatcher) — coerce scalars, arrays, and Series.
3+
* Outputs JSON: {"function": "to_numeric_generic", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { toNumeric, Series } from "../../src/index.ts";
6+
7+
const SIZE = 10_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const strNums = Array.from({ length: SIZE }, (_, i) => String(i * 0.1));
12+
const series = new Series({ data: strNums });
13+
14+
for (let i = 0; i < WARMUP; i++) {
15+
toNumeric("3.14");
16+
toNumeric(strNums.slice(0, 100), { errors: "coerce" });
17+
toNumeric(series, { errors: "coerce" });
18+
}
19+
20+
const times: number[] = [];
21+
for (let i = 0; i < ITERATIONS; i++) {
22+
const start = performance.now();
23+
toNumeric("3.14");
24+
toNumeric(strNums, { errors: "coerce" });
25+
toNumeric(series, { errors: "coerce" });
26+
times.push(performance.now() - start);
27+
}
28+
29+
const totalMs = times.reduce((a, b) => a + b, 0);
30+
const meanMs = totalMs / ITERATIONS;
31+
console.log(
32+
JSON.stringify({
33+
function: "to_numeric_generic",
34+
mean_ms: Math.round(meanMs * 1000) / 1000,
35+
iterations: ITERATIONS,
36+
total_ms: Math.round(totalMs * 1000) / 1000,
37+
}),
38+
);

0 commit comments

Comments
 (0)