Skip to content

Commit e4521f9

Browse files
Iteration 150: Add 5 benchmark pairs (473 total, +5 vs best 468)
Added benchmarks for: replaceSeries, isnull/notnull aliases, toNumericScalar, dataFrameAssign (functional API), dataFrameIsin (functional API). Run: https://github.com/githubnext/tsessebe/actions/runs/24561344321 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e3c731c commit e4521f9

10 files changed

Lines changed: 372 additions & 0 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Benchmark: DataFrame.assign — add new columns using the pandas assign API."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
df = pd.DataFrame({
10+
"a": [i * 1.0 for i in range(SIZE)],
11+
"b": [i * 2.0 for i in range(SIZE)],
12+
})
13+
14+
for _ in range(WARMUP):
15+
df.assign(
16+
c=[i * 3.0 for i in range(SIZE)],
17+
d=lambda working: working["a"] + working["c"],
18+
)
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
df.assign(
24+
c=[i * 3.0 for i in range(SIZE)],
25+
d=lambda working: working["a"] + working["c"],
26+
)
27+
times.append((time.perf_counter() - t0) * 1000)
28+
29+
total_ms = sum(times)
30+
print(json.dumps({"function": "dataframe_assign_fn", "mean_ms": round(total_ms / ITERATIONS, 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+
"""Benchmark: DataFrame.isin — test membership of each element against value sets."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
df = pd.DataFrame({
10+
"a": [i % 20 for i in range(SIZE)],
11+
"b": [["x", "y", "z", "w"][i % 4] for i in range(SIZE)],
12+
"c": [i % 10 for i in range(SIZE)],
13+
})
14+
15+
global_values = [0, 1, 2, "x", "y"]
16+
col_values = {"a": [0, 1, 2, 3, 4], "b": ["x", "y"], "c": [0, 5]}
17+
18+
for _ in range(WARMUP):
19+
df.isin(global_values)
20+
df.isin(col_values)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
df.isin(global_values)
26+
df.isin(col_values)
27+
times.append((time.perf_counter() - t0) * 1000)
28+
29+
total_ms = sum(times)
30+
print(json.dumps({"function": "dataframe_isin_fn", "mean_ms": round(total_ms / ITERATIONS, 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+
"""Benchmark: isnull / notnull — aliases for isna / notna on Series and DataFrame."""
2+
import json, time
3+
import numpy as np
4+
import pandas as pd
5+
6+
SIZE = 100_000
7+
WARMUP = 5
8+
ITERATIONS = 50
9+
10+
s = pd.Series([np.nan if i % 7 == 0 else i * 0.1 for i in range(SIZE)])
11+
df = pd.DataFrame({
12+
"a": [np.nan if i % 5 == 0 else float(i) for i in range(SIZE)],
13+
"b": [np.nan if i % 3 == 0 else i * 2.5 for i in range(SIZE)],
14+
})
15+
16+
for _ in range(WARMUP):
17+
pd.isnull(s)
18+
pd.notnull(s)
19+
pd.isnull(df)
20+
pd.notnull(df)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
pd.isnull(s)
26+
pd.notnull(s)
27+
pd.isnull(df)
28+
pd.notnull(df)
29+
times.append((time.perf_counter() - t0) * 1000)
30+
31+
total_ms = sum(times)
32+
print(json.dumps({"function": "isnull_notnull", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Benchmark: Series.replace — replace values in a Series."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
s = pd.Series([i % 10 for i in range(SIZE)])
10+
mapping = {0: 100, 1: 200, 2: 300, 3: 400, 4: 500}
11+
12+
for _ in range(WARMUP):
13+
s.replace(mapping)
14+
15+
times = []
16+
for _ in range(ITERATIONS):
17+
t0 = time.perf_counter()
18+
s.replace(mapping)
19+
times.append((time.perf_counter() - t0) * 1000)
20+
21+
total_ms = sum(times)
22+
print(json.dumps({"function": "replace_series", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Benchmark: pd.to_numeric scalar coercion — convert individual scalar values to numeric."""
2+
import json, time
3+
import pandas as pd
4+
5+
WARMUP = 5
6+
ITERATIONS = 100
7+
BATCH = 10_000
8+
9+
inputs = []
10+
for i in range(BATCH):
11+
r = i % 6
12+
if r == 0:
13+
inputs.append(str(i * 1.5))
14+
elif r == 1:
15+
inputs.append(i)
16+
elif r == 2:
17+
inputs.append(f" {i} ")
18+
elif r == 3:
19+
inputs.append(True)
20+
elif r == 4:
21+
inputs.append(None)
22+
else:
23+
inputs.append(str(i))
24+
25+
for _ in range(WARMUP):
26+
for v in inputs:
27+
pd.to_numeric(v, errors="coerce")
28+
29+
times = []
30+
for _ in range(ITERATIONS):
31+
t0 = time.perf_counter()
32+
for v in inputs:
33+
pd.to_numeric(v, errors="coerce")
34+
times.append((time.perf_counter() - t0) * 1000)
35+
36+
total_ms = sum(times)
37+
print(json.dumps({"function": "to_numeric_scalar", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Benchmark: dataFrameAssign — add new columns to a DataFrame using the functional API.
3+
* Outputs JSON: {"function": "dataframe_assign_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { dataFrameAssign, DataFrame, Series } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const df = new DataFrame({
12+
a: Array.from({ length: SIZE }, (_, i) => i * 1.0),
13+
b: Array.from({ length: SIZE }, (_, i) => i * 2.0),
14+
});
15+
const colC = new Series({ data: Array.from({ length: SIZE }, (_, i) => i * 3.0), name: "c" });
16+
17+
for (let i = 0; i < WARMUP; i++) {
18+
dataFrameAssign(df, {
19+
c: colC,
20+
d: (working) => {
21+
const aVals = working.col("a").values;
22+
const cVals = working.col("c").values;
23+
return new Series({ data: aVals.map((v, idx) => (v as number) + (cVals[idx] as number)) });
24+
},
25+
});
26+
}
27+
28+
const times: number[] = [];
29+
for (let i = 0; i < ITERATIONS; i++) {
30+
const start = performance.now();
31+
dataFrameAssign(df, {
32+
c: colC,
33+
d: (working) => {
34+
const aVals = working.col("a").values;
35+
const cVals = working.col("c").values;
36+
return new Series({ data: aVals.map((v, idx) => (v as number) + (cVals[idx] as number)) });
37+
},
38+
});
39+
times.push(performance.now() - start);
40+
}
41+
42+
const totalMs = times.reduce((a, b) => a + b, 0);
43+
const meanMs = totalMs / ITERATIONS;
44+
console.log(
45+
JSON.stringify({
46+
function: "dataframe_assign_fn",
47+
mean_ms: Math.round(meanMs * 1000) / 1000,
48+
iterations: ITERATIONS,
49+
total_ms: Math.round(totalMs * 1000) / 1000,
50+
}),
51+
);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Benchmark: dataFrameIsin — test membership of each element in a DataFrame against value sets.
3+
* Outputs JSON: {"function": "dataframe_isin_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { dataFrameIsin, DataFrame } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const df = new DataFrame({
12+
a: Array.from({ length: SIZE }, (_, i) => i % 20),
13+
b: Array.from({ length: SIZE }, (_, i) => ["x", "y", "z", "w"][i % 4]),
14+
c: Array.from({ length: SIZE }, (_, i) => i % 10),
15+
});
16+
17+
// Global isin — check all columns
18+
const globalValues = [0, 1, 2, "x", "y"];
19+
// Per-column isin dict
20+
const colValues = { a: [0, 1, 2, 3, 4], b: ["x", "y"], c: [0, 5] };
21+
22+
for (let i = 0; i < WARMUP; i++) {
23+
dataFrameIsin(df, globalValues);
24+
dataFrameIsin(df, colValues);
25+
}
26+
27+
const times: number[] = [];
28+
for (let i = 0; i < ITERATIONS; i++) {
29+
const start = performance.now();
30+
dataFrameIsin(df, globalValues);
31+
dataFrameIsin(df, colValues);
32+
times.push(performance.now() - start);
33+
}
34+
35+
const totalMs = times.reduce((a, b) => a + b, 0);
36+
const meanMs = totalMs / ITERATIONS;
37+
console.log(
38+
JSON.stringify({
39+
function: "dataframe_isin_fn",
40+
mean_ms: Math.round(meanMs * 1000) / 1000,
41+
iterations: ITERATIONS,
42+
total_ms: Math.round(totalMs * 1000) / 1000,
43+
}),
44+
);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Benchmark: isnull / notnull — aliases for isna / notna on Series and DataFrame.
3+
* Outputs JSON: {"function": "isnull_notnull", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { isnull, notnull, Series, DataFrame } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const s = new Series({
12+
data: Array.from({ length: SIZE }, (_, i) => (i % 7 === 0 ? null : i * 0.1)),
13+
});
14+
const df = new DataFrame({
15+
a: Array.from({ length: SIZE }, (_, i) => (i % 5 === 0 ? null : i)),
16+
b: Array.from({ length: SIZE }, (_, i) => (i % 3 === 0 ? null : i * 2.5)),
17+
});
18+
19+
for (let i = 0; i < WARMUP; i++) {
20+
isnull(s);
21+
notnull(s);
22+
isnull(df);
23+
notnull(df);
24+
}
25+
26+
const times: number[] = [];
27+
for (let i = 0; i < ITERATIONS; i++) {
28+
const start = performance.now();
29+
isnull(s);
30+
notnull(s);
31+
isnull(df);
32+
notnull(df);
33+
times.push(performance.now() - start);
34+
}
35+
36+
const totalMs = times.reduce((a, b) => a + b, 0);
37+
const meanMs = totalMs / ITERATIONS;
38+
console.log(
39+
JSON.stringify({
40+
function: "isnull_notnull",
41+
mean_ms: Math.round(meanMs * 1000) / 1000,
42+
iterations: ITERATIONS,
43+
total_ms: Math.round(totalMs * 1000) / 1000,
44+
}),
45+
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Benchmark: replaceSeries — replace values in a Series.
3+
* Outputs JSON: {"function": "replace_series", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { replaceSeries, Series } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const s = new Series({ data: Array.from({ length: SIZE }, (_, i) => i % 10) });
12+
const mapping = new Map<number, number>([
13+
[0, 100],
14+
[1, 200],
15+
[2, 300],
16+
[3, 400],
17+
[4, 500],
18+
]);
19+
20+
for (let i = 0; i < WARMUP; i++) {
21+
replaceSeries(s, mapping);
22+
}
23+
24+
const times: number[] = [];
25+
for (let i = 0; i < ITERATIONS; i++) {
26+
const start = performance.now();
27+
replaceSeries(s, mapping);
28+
times.push(performance.now() - start);
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: "replace_series",
36+
mean_ms: Math.round(meanMs * 1000) / 1000,
37+
iterations: ITERATIONS,
38+
total_ms: Math.round(totalMs * 1000) / 1000,
39+
}),
40+
);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Benchmark: toNumericScalar — coerce individual scalars to numeric values.
3+
* Outputs JSON: {"function": "to_numeric_scalar", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { toNumericScalar } from "../../src/index.ts";
6+
7+
const WARMUP = 5;
8+
const ITERATIONS = 100;
9+
const BATCH = 10_000;
10+
11+
const inputs: unknown[] = Array.from({ length: BATCH }, (_, i) => {
12+
const r = i % 6;
13+
if (r === 0) return String(i * 1.5);
14+
if (r === 1) return i;
15+
if (r === 2) return ` ${i} `;
16+
if (r === 3) return true;
17+
if (r === 4) return null;
18+
return String(i);
19+
});
20+
21+
for (let i = 0; i < WARMUP; i++) {
22+
for (const v of inputs) toNumericScalar(v, { errors: "coerce" });
23+
}
24+
25+
const times: number[] = [];
26+
for (let i = 0; i < ITERATIONS; i++) {
27+
const start = performance.now();
28+
for (const v of inputs) toNumericScalar(v, { errors: "coerce" });
29+
times.push(performance.now() - start);
30+
}
31+
32+
const totalMs = times.reduce((a, b) => a + b, 0);
33+
const meanMs = totalMs / ITERATIONS;
34+
console.log(
35+
JSON.stringify({
36+
function: "to_numeric_scalar",
37+
mean_ms: Math.round(meanMs * 1000) / 1000,
38+
iterations: ITERATIONS,
39+
total_ms: Math.round(totalMs * 1000) / 1000,
40+
}),
41+
);

0 commit comments

Comments
 (0)