Skip to content

Commit df6dab9

Browse files
Iteration 156: 5 new benchmark pairs
Added benchmarks for DateOffset rollforward/rollback/onOffset, more DateOffset types (MonthBegin/YearEnd/Week/Minute/Milli), date_range with various frequency options, combineFirstDataFrame standalone function, and SeriesGroupBy.agg with custom aggregate functions. Run: https://github.com/githubnext/tsessebe/actions/runs/24570329650 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 313b4f3 commit df6dab9

10 files changed

Lines changed: 475 additions & 0 deletions
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Benchmark: DataFrame.combine_first — fill NaN values from another DataFrame (union of indexes).
2+
Mirrors tsb bench_combine_first_dataframe.ts.
3+
"""
4+
import json, time
5+
import numpy as np
6+
import pandas as pd
7+
8+
SIZE = 5_000
9+
WARMUP = 5
10+
ITERATIONS = 30
11+
12+
rows1 = list(range(SIZE))
13+
data1a = [None if i % 3 == 0 else i * 1.5 for i in range(SIZE)]
14+
data1b = [None if i % 5 == 0 else i * 0.5 for i in range(SIZE)]
15+
df1 = pd.DataFrame({"a": data1a, "b": data1b}, index=rows1)
16+
17+
rows2 = list(range(SIZE + 500))
18+
data2a = [i * 2.0 for i in range(SIZE + 500)]
19+
data2b = [i * 1.0 for i in range(SIZE + 500)]
20+
data2c = [i * 0.1 for i in range(SIZE + 500)]
21+
df2 = pd.DataFrame({"a": data2a, "b": data2b, "c": data2c}, index=rows2)
22+
23+
for _ in range(WARMUP):
24+
df1.combine_first(df2)
25+
26+
start = time.perf_counter()
27+
for _ in range(ITERATIONS):
28+
df1.combine_first(df2)
29+
total = (time.perf_counter() - start) * 1000
30+
31+
print(json.dumps({
32+
"function": "combine_first_dataframe",
33+
"mean_ms": round(total / ITERATIONS, 3),
34+
"iterations": ITERATIONS,
35+
"total_ms": round(total, 3),
36+
}))
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Benchmark: DateOffset more types — MonthBegin, YearEnd, Week, Minute, Milli apply.
2+
Mirrors tsb bench_date_offset_more_types.ts for pandas.tseries.offsets.
3+
"""
4+
import json, time
5+
from datetime import timedelta
6+
import pandas as pd
7+
from pandas.tseries.offsets import MonthBegin, YearEnd, Week, Minute, Milli
8+
9+
SIZE = 5_000
10+
WARMUP = 5
11+
ITERATIONS = 50
12+
13+
month_begin = MonthBegin(1)
14+
year_end = YearEnd(1)
15+
week = Week(2)
16+
minute = Minute(60)
17+
milli = Milli(1000)
18+
19+
base = pd.Timestamp("2020-01-15 10:30:00", tz="UTC")
20+
dates = [base + timedelta(minutes=i) for i in range(SIZE)]
21+
22+
for _ in range(WARMUP):
23+
for d in dates[:100]:
24+
d + month_begin
25+
d + year_end
26+
d + week
27+
d + minute
28+
d + milli
29+
30+
start = time.perf_counter()
31+
for _ in range(ITERATIONS):
32+
for d in dates:
33+
d + month_begin
34+
d + year_end
35+
d + week
36+
d + minute
37+
d + milli
38+
total = (time.perf_counter() - start) * 1000
39+
40+
print(json.dumps({
41+
"function": "date_offset_more_types",
42+
"mean_ms": round(total / ITERATIONS, 3),
43+
"iterations": ITERATIONS,
44+
"total_ms": round(total, 3),
45+
}))
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Benchmark: DateOffset.rollforward / rollback / is_on_offset — snap dates to anchors.
2+
Mirrors tsb bench_date_offset_rollforward.ts for pandas.tseries.offsets.
3+
"""
4+
import json, time
5+
from datetime import datetime, timezone, timedelta
6+
from pandas.tseries.offsets import MonthEnd, BusinessDay, YearBegin, MonthBegin, YearEnd
7+
8+
SIZE = 5_000
9+
WARMUP = 5
10+
ITERATIONS = 50
11+
12+
month_end = MonthEnd(1)
13+
biz_day = BusinessDay(1)
14+
year_begin = YearBegin(1)
15+
month_begin = MonthBegin(1)
16+
year_end = YearEnd(1)
17+
18+
import pandas as pd
19+
base = pd.Timestamp("2020-01-15", tz="UTC")
20+
dates = [base + timedelta(days=i) for i in range(SIZE)]
21+
22+
for _ in range(WARMUP):
23+
for d in dates[:100]:
24+
month_end.rollforward(d)
25+
month_end.rollback(d)
26+
month_end.is_on_offset(d)
27+
biz_day.rollforward(d)
28+
biz_day.rollback(d)
29+
biz_day.is_on_offset(d)
30+
year_begin.rollforward(d)
31+
year_begin.rollback(d)
32+
month_begin.rollforward(d)
33+
month_begin.rollback(d)
34+
year_end.rollforward(d)
35+
year_end.rollback(d)
36+
37+
start = time.perf_counter()
38+
for _ in range(ITERATIONS):
39+
for d in dates:
40+
month_end.rollforward(d)
41+
month_end.rollback(d)
42+
month_end.is_on_offset(d)
43+
biz_day.rollforward(d)
44+
biz_day.rollback(d)
45+
year_begin.rollforward(d)
46+
year_begin.rollback(d)
47+
month_begin.rollforward(d)
48+
month_begin.rollback(d)
49+
year_end.rollforward(d)
50+
year_end.rollback(d)
51+
total = (time.perf_counter() - start) * 1000
52+
53+
print(json.dumps({
54+
"function": "date_offset_rollforward",
55+
"mean_ms": round(total / ITERATIONS, 3),
56+
"iterations": ITERATIONS,
57+
"total_ms": round(total, 3),
58+
}))
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Benchmark: date_range — generate DatetimeIndex with various frequency options.
2+
Mirrors tsb bench_date_range_options.ts using pandas.date_range.
3+
"""
4+
import json, time
5+
import pandas as pd
6+
7+
WARMUP = 5
8+
ITERATIONS = 100
9+
10+
for _ in range(WARMUP):
11+
pd.date_range(start="2020-01-01", periods=1_000, freq="D")
12+
pd.date_range(start="2020-01-01", periods=1_000, freq="h")
13+
pd.date_range(start="2020-01-01", periods=500, freq="ME")
14+
pd.date_range(start="2020-01-01", periods=200, freq="QE")
15+
pd.date_range(start="2020-01-01", periods=100, freq="YE")
16+
pd.date_range(start="2020-01-01", periods=500, freq="MS")
17+
pd.date_range(start="2020-01-01", end="2025-01-01", freq="W")
18+
pd.date_range(start="2020-01-01", periods=2_000, freq="min")
19+
20+
start = time.perf_counter()
21+
for _ in range(ITERATIONS):
22+
pd.date_range(start="2020-01-01", periods=1_000, freq="D")
23+
pd.date_range(start="2020-01-01", periods=1_000, freq="h")
24+
pd.date_range(start="2020-01-01", periods=500, freq="ME")
25+
pd.date_range(start="2020-01-01", periods=200, freq="QE")
26+
pd.date_range(start="2020-01-01", periods=100, freq="YE")
27+
pd.date_range(start="2020-01-01", periods=500, freq="MS")
28+
pd.date_range(start="2020-01-01", end="2025-01-01", freq="W")
29+
pd.date_range(start="2020-01-01", periods=2_000, freq="min")
30+
total = (time.perf_counter() - start) * 1000
31+
32+
print(json.dumps({
33+
"function": "date_range_options",
34+
"mean_ms": round(total / ITERATIONS, 3),
35+
"iterations": ITERATIONS,
36+
"total_ms": round(total, 3),
37+
}))
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Benchmark: SeriesGroupBy.agg with custom aggregate functions — median, range.
2+
Mirrors tsb bench_series_groupby_custom_agg.ts.
3+
"""
4+
import json, time
5+
import numpy as np
6+
import pandas as pd
7+
8+
SIZE = 100_000
9+
WARMUP = 3
10+
ITERATIONS = 20
11+
12+
data = [(i * 1.5) % 9999 for i in range(SIZE)]
13+
by = [i % 100 for i in range(SIZE)]
14+
s = pd.Series(data)
15+
gb = s.groupby(by)
16+
17+
def median_fn(x):
18+
return float(np.median(x))
19+
20+
def range_fn(x):
21+
return float(np.max(x) - np.min(x))
22+
23+
for _ in range(WARMUP):
24+
gb.agg(median_fn)
25+
gb.agg(range_fn)
26+
27+
start = time.perf_counter()
28+
for _ in range(ITERATIONS):
29+
gb.agg(median_fn)
30+
gb.agg(range_fn)
31+
total = (time.perf_counter() - start) * 1000
32+
33+
print(json.dumps({
34+
"function": "series_groupby_custom_agg",
35+
"mean_ms": round(total / ITERATIONS, 3),
36+
"iterations": ITERATIONS,
37+
"total_ms": round(total, 3),
38+
}))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Benchmark: combineFirstDataFrame — fill NaN values from another DataFrame (union of indexes).
3+
* Mirrors pandas DataFrame.combine_first.
4+
* Outputs JSON: {"function": "combine_first_dataframe", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
*/
6+
import { DataFrame, Index, combineFirstDataFrame } from "../../src/index.ts";
7+
8+
const SIZE = 5_000;
9+
const WARMUP = 5;
10+
const ITERATIONS = 30;
11+
12+
// df1: rows 0..SIZE-1, ~30% nulls
13+
const rows1 = Array.from({ length: SIZE }, (_, i) => i);
14+
const data1a = Array.from({ length: SIZE }, (_, i) => (i % 3 === 0 ? null : i * 1.5));
15+
const data1b = Array.from({ length: SIZE }, (_, i) => (i % 5 === 0 ? null : i * 0.5));
16+
const idx1 = new Index(rows1);
17+
const df1 = new DataFrame({ a: data1a, b: data1b }, idx1);
18+
19+
// df2: rows 0..SIZE+500-1 (overlapping + extra), fills missing in df1
20+
const rows2 = Array.from({ length: SIZE + 500 }, (_, i) => i);
21+
const data2a = Array.from({ length: SIZE + 500 }, (_, i) => i * 2.0);
22+
const data2b = Array.from({ length: SIZE + 500 }, (_, i) => i * 1.0);
23+
const data2c = Array.from({ length: SIZE + 500 }, (_, i) => i * 0.1);
24+
const idx2 = new Index(rows2);
25+
const df2 = new DataFrame({ a: data2a, b: data2b, c: data2c }, idx2);
26+
27+
for (let i = 0; i < WARMUP; i++) {
28+
combineFirstDataFrame(df1, df2);
29+
}
30+
31+
const start = performance.now();
32+
for (let i = 0; i < ITERATIONS; i++) {
33+
combineFirstDataFrame(df1, df2);
34+
}
35+
const total = performance.now() - start;
36+
37+
console.log(
38+
JSON.stringify({
39+
function: "combine_first_dataframe",
40+
mean_ms: Math.round((total / ITERATIONS) * 1000) / 1000,
41+
iterations: ITERATIONS,
42+
total_ms: Math.round(total * 1000) / 1000,
43+
}),
44+
);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Benchmark: DateOffset more types — apply operations for MonthBegin, YearEnd, Week, Minute, Milli.
3+
* These DateOffset classes haven't been covered in existing benchmarks.
4+
* Outputs JSON: {"function": "date_offset_more_types", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
*/
6+
import { MonthBegin, YearEnd, Week, Minute, Milli } from "../../src/index.ts";
7+
8+
const SIZE = 5_000;
9+
const WARMUP = 5;
10+
const ITERATIONS = 50;
11+
12+
const monthBegin = new MonthBegin(1);
13+
const yearEnd = new YearEnd(1);
14+
const week = new Week(2);
15+
const minute = new Minute(60);
16+
const milli = new Milli(1000);
17+
18+
const base = new Date(Date.UTC(2020, 0, 15, 10, 30, 0));
19+
const dates = Array.from({ length: SIZE }, (_, i) => new Date(base.getTime() + i * 60_000));
20+
21+
for (let i = 0; i < WARMUP; i++) {
22+
for (const d of dates.slice(0, 100)) {
23+
monthBegin.apply(d);
24+
yearEnd.apply(d);
25+
week.apply(d);
26+
minute.apply(d);
27+
milli.apply(d);
28+
}
29+
}
30+
31+
const start = performance.now();
32+
for (let i = 0; i < ITERATIONS; i++) {
33+
for (const d of dates) {
34+
monthBegin.apply(d);
35+
yearEnd.apply(d);
36+
week.apply(d);
37+
minute.apply(d);
38+
milli.apply(d);
39+
}
40+
}
41+
const total = performance.now() - start;
42+
43+
console.log(
44+
JSON.stringify({
45+
function: "date_offset_more_types",
46+
mean_ms: Math.round((total / ITERATIONS) * 1000) / 1000,
47+
iterations: ITERATIONS,
48+
total_ms: Math.round(total * 1000) / 1000,
49+
}),
50+
);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Benchmark: DateOffset.rollforward / rollback / onOffset — snap dates to offset anchors.
3+
* Tests MonthEnd, BusinessDay, YearBegin, MonthBegin, YearEnd.
4+
* Outputs JSON: {"function": "date_offset_rollforward", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
*/
6+
import { MonthEnd, BusinessDay, YearBegin, MonthBegin, YearEnd } from "../../src/index.ts";
7+
8+
const SIZE = 5_000;
9+
const WARMUP = 5;
10+
const ITERATIONS = 50;
11+
12+
const monthEnd = new MonthEnd(1);
13+
const bizDay = new BusinessDay(1);
14+
const yearBegin = new YearBegin(1);
15+
const monthBegin = new MonthBegin(1);
16+
const yearEnd = new YearEnd(1);
17+
18+
const base = new Date(Date.UTC(2020, 0, 15));
19+
const dates = Array.from({ length: SIZE }, (_, i) => new Date(base.getTime() + i * 86_400_000));
20+
21+
for (let i = 0; i < WARMUP; i++) {
22+
for (const d of dates.slice(0, 100)) {
23+
monthEnd.rollforward(d);
24+
monthEnd.rollback(d);
25+
monthEnd.onOffset(d);
26+
bizDay.rollforward(d);
27+
bizDay.rollback(d);
28+
bizDay.onOffset(d);
29+
yearBegin.rollforward(d);
30+
yearBegin.rollback(d);
31+
monthBegin.rollforward(d);
32+
monthBegin.rollback(d);
33+
yearEnd.rollforward(d);
34+
yearEnd.rollback(d);
35+
}
36+
}
37+
38+
const start = performance.now();
39+
for (let i = 0; i < ITERATIONS; i++) {
40+
for (const d of dates) {
41+
monthEnd.rollforward(d);
42+
monthEnd.rollback(d);
43+
monthEnd.onOffset(d);
44+
bizDay.rollforward(d);
45+
bizDay.rollback(d);
46+
yearBegin.rollforward(d);
47+
yearBegin.rollback(d);
48+
monthBegin.rollforward(d);
49+
monthBegin.rollback(d);
50+
yearEnd.rollforward(d);
51+
yearEnd.rollback(d);
52+
}
53+
}
54+
const total = performance.now() - start;
55+
56+
console.log(
57+
JSON.stringify({
58+
function: "date_offset_rollforward",
59+
mean_ms: Math.round((total / ITERATIONS) * 1000) / 1000,
60+
iterations: ITERATIONS,
61+
total_ms: Math.round(total * 1000) / 1000,
62+
}),
63+
);

0 commit comments

Comments
 (0)