Skip to content

Commit a84aca2

Browse files
Iteration 143: Add 8 benchmark pairs (437 total, +8 vs 429)
Add standalone functional-form benchmarks and new operation benchmarks: - bench_quantile_fn: quantileSeries/quantileDataFrame standalone functions - bench_pct_change_fn: pctChangeSeries/pctChangeDataFrame standalone functions - bench_merge_suffixes: merge with custom suffixes option - bench_expanding_min_periods: Expanding with minPeriods option - bench_dt_isocalendar: DatetimeAccessor.isocalendar_week - bench_period_asfreq: Period.asfreq/PeriodIndex.asfreq frequency conversion - bench_sample_fn: sampleSeries/sampleDataFrame standalone functions - bench_nunique_fn: nuniqueSeries/nuniqueDataFrame standalone functions Run: https://github.com/githubnext/tsessebe/actions/runs/24547746540 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 081cb30 commit a84aca2

16 files changed

Lines changed: 599 additions & 0 deletions
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""
2+
Benchmark: pandas DatetimeIndex.isocalendar().week on 100k dates.
3+
Outputs JSON: {"function": "dt_isocalendar", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
ROWS = 100_000
10+
WARMUP = 3
11+
ITERATIONS = 10
12+
13+
dates = pd.date_range("2000-01-01", periods=ROWS, freq="D")
14+
s = pd.Series(dates)
15+
16+
for _ in range(WARMUP):
17+
s.dt.isocalendar()["week"]
18+
19+
times = []
20+
for _ in range(ITERATIONS):
21+
t0 = time.perf_counter()
22+
s.dt.isocalendar()["week"]
23+
times.append((time.perf_counter() - t0) * 1000)
24+
25+
total_ms = sum(times)
26+
mean_ms = total_ms / ITERATIONS
27+
print(json.dumps({"function": "dt_isocalendar", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Benchmark: pandas Expanding with min_periods option.
3+
Outputs JSON: {"function": "expanding_min_periods", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
import numpy as np
9+
10+
ROWS = 100_000
11+
WARMUP = 3
12+
ITERATIONS = 10
13+
14+
data = [float('nan') if i % 10 == 0 else float(np.sin(i * 0.01)) for i in range(ROWS)]
15+
s = pd.Series(data)
16+
17+
for _ in range(WARMUP):
18+
s.expanding(min_periods=10).mean()
19+
s.expanding(min_periods=50).sum()
20+
s.expanding(min_periods=5).std()
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
s.expanding(min_periods=10).mean()
26+
s.expanding(min_periods=50).sum()
27+
s.expanding(min_periods=5).std()
28+
times.append((time.perf_counter() - t0) * 1000)
29+
30+
total_ms = sum(times)
31+
mean_ms = total_ms / ITERATIONS
32+
print(json.dumps({"function": "expanding_min_periods", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Benchmark: pandas merge with custom suffixes option.
3+
Outputs JSON: {"function": "merge_suffixes", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
ROWS = 50_000
10+
WARMUP = 3
11+
ITERATIONS = 10
12+
13+
ids = [i % 10_000 for i in range(ROWS)]
14+
left = pd.DataFrame({"id": ids, "value": [x * 1.1 for x in ids], "score": [x * 0.5 for x in ids]})
15+
right = pd.DataFrame({
16+
"id": list(range(10_000)),
17+
"value": [i * 2.0 for i in range(10_000)],
18+
"rank": list(range(10_000)),
19+
})
20+
21+
for _ in range(WARMUP):
22+
pd.merge(left, right, on="id", suffixes=("_left", "_right"))
23+
pd.merge(left, right, on="id", how="outer", suffixes=("_l", "_r"))
24+
25+
times = []
26+
for _ in range(ITERATIONS):
27+
t0 = time.perf_counter()
28+
pd.merge(left, right, on="id", suffixes=("_left", "_right"))
29+
pd.merge(left, right, on="id", how="outer", suffixes=("_l", "_r"))
30+
times.append((time.perf_counter() - t0) * 1000)
31+
32+
total_ms = sum(times)
33+
mean_ms = total_ms / ITERATIONS
34+
print(json.dumps({"function": "merge_suffixes", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Benchmark: pandas nunique on Series and DataFrame (functional-form equivalent).
3+
Outputs JSON: {"function": "nunique_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
import numpy as np
9+
10+
ROWS = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 20
13+
14+
low = pd.Series([i % 1000 for i in range(ROWS)])
15+
high = pd.Series([i % 50_000 for i in range(ROWS)])
16+
with_nulls = pd.Series([float('nan') if i % 100 == 0 else i % 2000 for i in range(ROWS)])
17+
df = pd.DataFrame({"a": low, "b": high, "c": with_nulls})
18+
19+
for _ in range(WARMUP):
20+
low.nunique()
21+
with_nulls.nunique(dropna=False)
22+
df.nunique()
23+
24+
times = []
25+
for _ in range(ITERATIONS):
26+
t0 = time.perf_counter()
27+
low.nunique()
28+
with_nulls.nunique(dropna=False)
29+
df.nunique()
30+
times.append((time.perf_counter() - t0) * 1000)
31+
32+
total_ms = sum(times)
33+
mean_ms = total_ms / ITERATIONS
34+
print(json.dumps({"function": "nunique_fn", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Benchmark: pandas pct_change on Series and DataFrame.
3+
Outputs JSON: {"function": "pct_change_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
ROWS = 100_000
10+
WARMUP = 3
11+
ITERATIONS = 10
12+
13+
data = [i * 1.1 + 1.0 for i in range(ROWS)]
14+
s = pd.Series(data)
15+
df = pd.DataFrame({"a": data, "b": [x * 2 for x in data]})
16+
17+
for _ in range(WARMUP):
18+
s.pct_change()
19+
s.pct_change(periods=2)
20+
df.pct_change()
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
s.pct_change()
26+
s.pct_change(periods=2)
27+
df.pct_change()
28+
times.append((time.perf_counter() - t0) * 1000)
29+
30+
total_ms = sum(times)
31+
mean_ms = total_ms / ITERATIONS
32+
print(json.dumps({"function": "pct_change_fn", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Benchmark: pandas Period.asfreq and PeriodIndex.asfreq — frequency conversion.
3+
Outputs JSON: {"function": "period_asfreq", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
SIZE = 10_000
10+
WARMUP = 3
11+
ITERATIONS = 20
12+
13+
idx = pd.period_range(start="2000-01", periods=SIZE, freq="M")
14+
15+
for _ in range(WARMUP):
16+
idx.asfreq("D", how="start")
17+
idx.asfreq("D", how="end")
18+
idx.asfreq("Q", how="start")
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
idx.asfreq("D", how="start")
24+
idx.asfreq("D", how="end")
25+
idx.asfreq("Q", how="start")
26+
times.append((time.perf_counter() - t0) * 1000)
27+
28+
total_ms = sum(times)
29+
mean_ms = total_ms / ITERATIONS
30+
print(json.dumps({"function": "period_asfreq", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Benchmark: quantileSeries / quantileDataFrame equivalent — pandas Series.quantile / DataFrame.quantile.
3+
Outputs JSON: {"function": "quantile_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
import numpy as np
9+
10+
ROWS = 100_000
11+
WARMUP = 3
12+
ITERATIONS = 10
13+
14+
data = [(i * 1.41) % 10000 for i in range(ROWS)]
15+
s = pd.Series(data)
16+
df = pd.DataFrame({"a": data, "b": [x * 2 for x in data], "c": [x * 0.5 for x in data]})
17+
18+
for _ in range(WARMUP):
19+
s.quantile(0.25)
20+
s.quantile([0.1, 0.5, 0.9])
21+
df.quantile(0.5)
22+
23+
times = []
24+
for _ in range(ITERATIONS):
25+
t0 = time.perf_counter()
26+
s.quantile(0.25)
27+
s.quantile([0.1, 0.5, 0.9])
28+
df.quantile(0.5)
29+
times.append((time.perf_counter() - t0) * 1000)
30+
31+
total_ms = sum(times)
32+
mean_ms = total_ms / ITERATIONS
33+
print(json.dumps({"function": "quantile_fn", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Benchmark: pandas sample on Series and DataFrame.
3+
Outputs JSON: {"function": "sample_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
ROWS = 100_000
10+
WARMUP = 3
11+
ITERATIONS = 20
12+
13+
data = [i * 1.5 for i in range(ROWS)]
14+
s = pd.Series(data)
15+
df = pd.DataFrame({"a": data, "b": [x * 2 for x in data], "c": [x + 100 for x in data]})
16+
17+
for _ in range(WARMUP):
18+
s.sample(n=1000)
19+
s.sample(frac=0.01)
20+
df.sample(n=500)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
s.sample(n=1000)
26+
s.sample(frac=0.01)
27+
df.sample(n=500)
28+
times.append((time.perf_counter() - t0) * 1000)
29+
30+
total_ms = sum(times)
31+
mean_ms = total_ms / ITERATIONS
32+
print(json.dumps({"function": "sample_fn", "mean_ms": round(mean_ms, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Benchmark: DatetimeAccessor.isocalendar_week on 100k datetime Series.
3+
* Outputs JSON: {"function": "dt_isocalendar", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Series } from "../../src/index.ts";
6+
7+
const ROWS = 100_000;
8+
const WARMUP = 3;
9+
const ITERATIONS = 10;
10+
11+
// Dates spanning ~274 years to cover all ISO week patterns
12+
const base = new Date("2000-01-01").getTime();
13+
const dates = Array.from({ length: ROWS }, (_, i) => new Date(base + i * 86_400_000));
14+
const s = new Series({ data: dates });
15+
16+
for (let i = 0; i < WARMUP; i++) {
17+
s.dt.isocalendar_week();
18+
}
19+
20+
const times: number[] = [];
21+
for (let i = 0; i < ITERATIONS; i++) {
22+
const t0 = performance.now();
23+
s.dt.isocalendar_week();
24+
times.push(performance.now() - t0);
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: "dt_isocalendar",
32+
mean_ms: Math.round(meanMs * 1000) / 1000,
33+
iterations: ITERATIONS,
34+
total_ms: Math.round(totalMs * 1000) / 1000,
35+
}),
36+
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Benchmark: Expanding with minPeriods option.
3+
* Outputs JSON: {"function": "expanding_min_periods", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Series } from "../../src/index.ts";
6+
7+
const ROWS = 100_000;
8+
const WARMUP = 3;
9+
const ITERATIONS = 10;
10+
11+
const data = Array.from({ length: ROWS }, (_, i) => (i % 10 === 0 ? null : Math.sin(i * 0.01)));
12+
const s = new Series({ data });
13+
14+
for (let i = 0; i < WARMUP; i++) {
15+
s.expanding(10).mean();
16+
s.expanding(50).sum();
17+
s.expanding(5).std();
18+
}
19+
20+
const times: number[] = [];
21+
for (let i = 0; i < ITERATIONS; i++) {
22+
const t0 = performance.now();
23+
s.expanding(10).mean();
24+
s.expanding(50).sum();
25+
s.expanding(5).std();
26+
times.push(performance.now() - t0);
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: "expanding_min_periods",
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)