Skip to content

Commit 4edd601

Browse files
Iteration 161: 5 new benchmark pairs (513 total, +5 vs best 508)
Run: https://github.com/githubnext/tsessebe/actions/runs/24581386899 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0258e6f commit 4edd601

10 files changed

Lines changed: 397 additions & 0 deletions
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Benchmark: pandas DataFrame.clip with Series bounds (axis=0) on 100k-row DataFrame.
3+
Outputs JSON: {"function": "clip_dataframe_with_bounds", "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 = 30
13+
14+
df = pd.DataFrame({
15+
"a": [(i % 200) - 100 for i in range(SIZE)],
16+
"b": [(i % 150) - 75 for i in range(SIZE)],
17+
"c": [(i % 100) - 50 for i in range(SIZE)],
18+
})
19+
20+
lower_bounds = pd.Series([(i % 40) - 20 for i in range(SIZE)])
21+
upper_bounds = pd.Series([(i % 40) + 20 for i in range(SIZE)])
22+
23+
for _ in range(WARMUP):
24+
df.clip(lower=lower_bounds, upper=upper_bounds, axis=0)
25+
26+
times = []
27+
for _ in range(ITERATIONS):
28+
t0 = time.perf_counter()
29+
df.clip(lower=lower_bounds, upper=upper_bounds, axis=0)
30+
times.append((time.perf_counter() - t0) * 1000)
31+
32+
total_ms = sum(times)
33+
mean_ms = total_ms / ITERATIONS
34+
print(json.dumps({
35+
"function": "clip_dataframe_with_bounds",
36+
"mean_ms": mean_ms,
37+
"iterations": ITERATIONS,
38+
"total_ms": total_ms,
39+
}))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Benchmark: pandas Series.clip with per-element Series bounds on 100k values.
3+
Outputs JSON: {"function": "clip_series_with_bounds", "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 = 30
13+
14+
data = [(i % 200) - 100 for i in range(SIZE)]
15+
lower = pd.Series([(i % 50) - 30 for i in range(SIZE)])
16+
upper = pd.Series([(i % 50) + 20 for i in range(SIZE)])
17+
series = pd.Series(data)
18+
19+
for _ in range(WARMUP):
20+
series.clip(lower=lower, upper=upper)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
series.clip(lower=lower, upper=upper)
26+
times.append((time.perf_counter() - t0) * 1000)
27+
28+
total_ms = sum(times)
29+
mean_ms = total_ms / ITERATIONS
30+
print(json.dumps({
31+
"function": "clip_series_with_bounds",
32+
"mean_ms": mean_ms,
33+
"iterations": ITERATIONS,
34+
"total_ms": total_ms,
35+
}))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
Benchmark: pandas DataFrame.pipe with positional target argument on 100k-row DataFrame.
3+
Mirrors tsb's dataFramePipeTo — inserting the DataFrame at a specific arg position.
4+
Outputs JSON: {"function": "dataframe_pipe_to", "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+
WARMUP = 5
13+
ITERATIONS = 50
14+
15+
16+
def filter_above(threshold: float, df: pd.DataFrame) -> pd.DataFrame:
17+
return df[df["val"] > threshold]
18+
19+
20+
left = pd.DataFrame({
21+
"key": [i % 1000 for i in range(SIZE)],
22+
"val": [i * 1.5 for i in range(SIZE)],
23+
})
24+
25+
for _ in range(WARMUP):
26+
# pandas pipe with tuple form: (fn, 'positional_kwarg') — use pipe with lambda here
27+
left.pipe(lambda df: filter_above(50_000, df))
28+
29+
times = []
30+
for _ in range(ITERATIONS):
31+
t0 = time.perf_counter()
32+
left.pipe(lambda df: filter_above(50_000, df))
33+
times.append((time.perf_counter() - t0) * 1000)
34+
35+
total_ms = sum(times)
36+
mean_ms = total_ms / ITERATIONS
37+
print(json.dumps({
38+
"function": "dataframe_pipe_to",
39+
"mean_ms": mean_ms,
40+
"iterations": ITERATIONS,
41+
"total_ms": total_ms,
42+
}))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Benchmark: pandas qcut with IntervalIndex output on 100k values.
3+
Outputs JSON: {"function": "qcut_interval_index", "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 = 30
13+
14+
data = (np.arange(SIZE) * 1.1) % 1000
15+
16+
for _ in range(WARMUP):
17+
pd.qcut(data, q=10, duplicates="drop", retbins=True)
18+
19+
times = []
20+
for _ in range(ITERATIONS):
21+
t0 = time.perf_counter()
22+
pd.qcut(data, q=10, duplicates="drop", retbins=True)
23+
times.append((time.perf_counter() - t0) * 1000)
24+
25+
total_ms = sum(times)
26+
mean_ms = total_ms / ITERATIONS
27+
print(json.dumps({
28+
"function": "qcut_interval_index",
29+
"mean_ms": mean_ms,
30+
"iterations": ITERATIONS,
31+
"total_ms": total_ms,
32+
}))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
Benchmark: pandas Series/DataFrame log2 / log10 on 100k values.
3+
Outputs JSON: {"function": "series_log2_log10", "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 = 30
13+
14+
data = (np.arange(1, SIZE + 1) * 0.01)
15+
s = pd.Series(data)
16+
df = pd.DataFrame({
17+
"a": data,
18+
"b": np.arange(1, SIZE + 1) * 0.02,
19+
"c": np.arange(1, SIZE + 1) * 0.03,
20+
})
21+
22+
for _ in range(WARMUP):
23+
np.log2(s)
24+
np.log10(s)
25+
np.log2(df)
26+
np.log10(df)
27+
28+
times = []
29+
for _ in range(ITERATIONS):
30+
t0 = time.perf_counter()
31+
np.log2(s)
32+
np.log10(s)
33+
np.log2(df)
34+
np.log10(df)
35+
times.append((time.perf_counter() - t0) * 1000)
36+
37+
total_ms = sum(times)
38+
mean_ms = total_ms / ITERATIONS
39+
print(json.dumps({
40+
"function": "series_log2_log10",
41+
"mean_ms": mean_ms,
42+
"iterations": ITERATIONS,
43+
"total_ms": total_ms,
44+
}))
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Benchmark: clipDataFrameWithBounds with Series bounds (axis=0) on 100k-row DataFrame.
3+
* Outputs JSON: {"function": "clip_dataframe_with_bounds", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Series, DataFrame, clipDataFrameWithBounds } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
const df = DataFrame.fromColumns({
12+
a: Array.from({ length: SIZE }, (_, i) => (i % 200) - 100),
13+
b: Array.from({ length: SIZE }, (_, i) => (i % 150) - 75),
14+
c: Array.from({ length: SIZE }, (_, i) => (i % 100) - 50),
15+
});
16+
17+
const lowerBounds = new Series({ data: Array.from({ length: SIZE }, (_, i) => (i % 40) - 20) });
18+
const upperBounds = new Series({ data: Array.from({ length: SIZE }, (_, i) => (i % 40) + 20) });
19+
20+
for (let i = 0; i < WARMUP; i++) {
21+
clipDataFrameWithBounds(df, { lower: lowerBounds, upper: upperBounds, axis: 0 });
22+
}
23+
24+
const times: number[] = [];
25+
for (let i = 0; i < ITERATIONS; i++) {
26+
const t0 = performance.now();
27+
clipDataFrameWithBounds(df, { lower: lowerBounds, upper: upperBounds, axis: 0 });
28+
times.push(performance.now() - t0);
29+
}
30+
31+
const totalMs = times.reduce((a, b) => a + b, 0);
32+
const meanMs = totalMs / ITERATIONS;
33+
console.log(
34+
JSON.stringify({
35+
function: "clip_dataframe_with_bounds",
36+
mean_ms: meanMs,
37+
iterations: ITERATIONS,
38+
total_ms: totalMs,
39+
}),
40+
);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Benchmark: clipSeriesWithBounds with per-element Series bounds on 100k values.
3+
* Outputs JSON: {"function": "clip_series_with_bounds", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Series, clipSeriesWithBounds } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
const data = Array.from({ length: SIZE }, (_, i) => (i % 200) - 100);
12+
const lower = Array.from({ length: SIZE }, (_, i) => (i % 50) - 30);
13+
const upper = Array.from({ length: SIZE }, (_, i) => (i % 50) + 20);
14+
15+
const series = new Series({ data });
16+
const lowerSeries = new Series({ data: lower });
17+
const upperSeries = new Series({ data: upper });
18+
19+
for (let i = 0; i < WARMUP; i++) {
20+
clipSeriesWithBounds(series, { lower: lowerSeries, upper: upperSeries });
21+
}
22+
23+
const times: number[] = [];
24+
for (let i = 0; i < ITERATIONS; i++) {
25+
const t0 = performance.now();
26+
clipSeriesWithBounds(series, { lower: lowerSeries, upper: upperSeries });
27+
times.push(performance.now() - t0);
28+
}
29+
30+
const totalMs = times.reduce((a, b) => a + b, 0);
31+
const meanMs = totalMs / ITERATIONS;
32+
console.log(
33+
JSON.stringify({
34+
function: "clip_series_with_bounds",
35+
mean_ms: meanMs,
36+
iterations: ITERATIONS,
37+
total_ms: totalMs,
38+
}),
39+
);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Benchmark: dataFramePipeTo — insert DataFrame at a specific argument position in a pipeline.
3+
* Outputs JSON: {"function": "dataframe_pipe_to", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { DataFrame, dataFramePipeTo } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const left = DataFrame.fromColumns({
12+
key: Array.from({ length: SIZE }, (_, i) => i % 1000),
13+
val: Array.from({ length: SIZE }, (_, i) => i * 1.5),
14+
});
15+
16+
const right = DataFrame.fromColumns({
17+
key: Array.from({ length: 1000 }, (_, i) => i),
18+
label: Array.from({ length: 1000 }, (_, i) => `item_${i}`),
19+
});
20+
21+
// A simple transform: filter df rows where col > threshold
22+
function filterAbove(threshold: number, df: DataFrame): DataFrame {
23+
return df.filter((row) => (row["val"] as number) > threshold);
24+
}
25+
26+
for (let i = 0; i < WARMUP; i++) {
27+
// dataFramePipeTo inserts `left` at position 1: filterAbove(threshold, left)
28+
dataFramePipeTo(left, 1, filterAbove, 50_000);
29+
}
30+
31+
const times: number[] = [];
32+
for (let i = 0; i < ITERATIONS; i++) {
33+
const t0 = performance.now();
34+
dataFramePipeTo(left, 1, filterAbove, 50_000);
35+
times.push(performance.now() - t0);
36+
}
37+
38+
const totalMs = times.reduce((a, b) => a + b, 0);
39+
const meanMs = totalMs / ITERATIONS;
40+
console.log(
41+
JSON.stringify({
42+
function: "dataframe_pipe_to",
43+
mean_ms: meanMs,
44+
iterations: ITERATIONS,
45+
total_ms: totalMs,
46+
}),
47+
);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Benchmark: qcutIntervalIndex — compute quantile-based IntervalIndex from 100k values.
3+
* Outputs JSON: {"function": "qcut_interval_index", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { qcutIntervalIndex } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
const data = Array.from({ length: SIZE }, (_, i) => (i * 1.1) % 1000);
12+
13+
// Quantile-based binning into 10 equal-frequency bins
14+
for (let i = 0; i < WARMUP; i++) {
15+
qcutIntervalIndex(data, 10);
16+
}
17+
18+
const times: number[] = [];
19+
for (let i = 0; i < ITERATIONS; i++) {
20+
const t0 = performance.now();
21+
qcutIntervalIndex(data, 10);
22+
times.push(performance.now() - t0);
23+
}
24+
25+
const totalMs = times.reduce((a, b) => a + b, 0);
26+
const meanMs = totalMs / ITERATIONS;
27+
console.log(
28+
JSON.stringify({
29+
function: "qcut_interval_index",
30+
mean_ms: meanMs,
31+
iterations: ITERATIONS,
32+
total_ms: totalMs,
33+
}),
34+
);

0 commit comments

Comments
 (0)