Skip to content

Commit ba235be

Browse files
Iteration 157: 5 new benchmark pairs (503 total, +5 vs best 498)
Added benchmarks for: - nan_agg_extended: nancount/nanprod/nanmedian (extended nan aggregates) - rank_methods: rankSeries with min/max/first/dense tie-breaking methods - dropna_advanced: dropnaDataFrame with thresh/subset/axis=1 options - get_dummies_opts: getDummies/dataFrameGetDummies with prefix/dropFirst/dummyNa - factorize_sort: factorize/seriesFactorize with sort=true/useNaSentinel options Run: https://github.com/githubnext/tsessebe/actions/runs/24572885192 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent df6dab9 commit ba235be

10 files changed

Lines changed: 322 additions & 0 deletions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Benchmark: DataFrame.dropna with advanced options (thresh, subset, axis=1).
3+
Outputs JSON: {"function": "dropna_advanced", "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 = 10_000
11+
WARMUP = 5
12+
ITERATIONS = 30
13+
14+
# DataFrame with scattered null values
15+
rng = np.random.default_rng(42)
16+
df = pd.DataFrame({
17+
"a": [None if i % 4 == 0 else i * 0.1 for i in range(SIZE)],
18+
"b": [None if i % 6 == 0 else i * 2.0 for i in range(SIZE)],
19+
"c": [None if i % 8 == 0 else i % 100 for i in range(SIZE)],
20+
"d": [None if i % 3 == 0 else f"val_{i % 20}" for i in range(SIZE)],
21+
})
22+
23+
for _ in range(WARMUP):
24+
df.dropna(thresh=3)
25+
df.dropna(subset=["a", "b"])
26+
df.dropna(axis=1)
27+
28+
start = time.perf_counter()
29+
for _ in range(ITERATIONS):
30+
df.dropna(thresh=3)
31+
df.dropna(subset=["a", "b"])
32+
df.dropna(axis=1)
33+
total = (time.perf_counter() - start) * 1000
34+
35+
print(json.dumps({"function": "dropna_advanced", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total}))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Benchmark: pandas.factorize / Series.factorize with sort=True and use_na_sentinel options.
3+
Outputs JSON: {"function": "factorize_sort", "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+
categories = ["zebra", "apple", "mango", "banana", "coconut", "date"]
15+
data = [None if i % 15 == 0 else categories[i % len(categories)] for i in range(SIZE)]
16+
s = pd.Series(data, dtype="object")
17+
18+
for _ in range(WARMUP):
19+
pd.factorize(data, sort=True)
20+
pd.factorize(data, sort=True, use_na_sentinel=True)
21+
s.factorize(sort=True)
22+
23+
start = time.perf_counter()
24+
for _ in range(ITERATIONS):
25+
pd.factorize(data, sort=True)
26+
pd.factorize(data, sort=True, use_na_sentinel=True)
27+
s.factorize(sort=True)
28+
total = (time.perf_counter() - start) * 1000
29+
30+
print(json.dumps({"function": "factorize_sort", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total}))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Benchmark: pandas.get_dummies with prefix, drop_first, dummy_na options.
3+
Outputs JSON: {"function": "get_dummies_opts", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
SIZE = 10_000
10+
WARMUP = 5
11+
ITERATIONS = 30
12+
13+
categories = ["apple", "banana", "cherry", "date", "elderberry"]
14+
data = [None if i % 20 == 0 else categories[i % len(categories)] for i in range(SIZE)]
15+
s = pd.Series(data, dtype="object")
16+
17+
df = pd.DataFrame({
18+
"fruit": [None if i % 20 == 0 else categories[i % len(categories)] for i in range(SIZE)],
19+
"color": [["red", "green", "blue"][i % 3] for i in range(SIZE)],
20+
})
21+
22+
for _ in range(WARMUP):
23+
pd.get_dummies(s, prefix="cat", dummy_na=True)
24+
pd.get_dummies(s, drop_first=True)
25+
pd.get_dummies(df, columns=["fruit", "color"], prefix="col", drop_first=True)
26+
27+
start = time.perf_counter()
28+
for _ in range(ITERATIONS):
29+
pd.get_dummies(s, prefix="cat", dummy_na=True)
30+
pd.get_dummies(s, drop_first=True)
31+
pd.get_dummies(df, columns=["fruit", "color"], prefix="col", drop_first=True)
32+
total = (time.perf_counter() - start) * 1000
33+
34+
print(json.dumps({"function": "get_dummies_opts", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total}))
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
Benchmark: np.count_nonzero / np.nanprod / np.nanmedian — extended nan-ignoring aggregates.
3+
Outputs JSON: {"function": "nan_agg_extended", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import math
7+
import time
8+
import numpy as np
9+
10+
SIZE = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 50
13+
14+
# Array with ~15% NaN values
15+
data = np.array([float("nan") if i % 7 == 0 else math.cos(i * 0.02) * 50 + 1 for i in range(SIZE)])
16+
17+
for _ in range(WARMUP):
18+
np.sum(~np.isnan(data))
19+
np.nanprod(data[:1000])
20+
np.nanmedian(data)
21+
22+
start = time.perf_counter()
23+
for _ in range(ITERATIONS):
24+
np.sum(~np.isnan(data))
25+
np.nanprod(data[:1000])
26+
np.nanmedian(data)
27+
total = (time.perf_counter() - start) * 1000
28+
29+
print(json.dumps({"function": "nan_agg_extended", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total}))
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Benchmark: Series.rank with different tie-breaking methods (min/max/first/dense).
3+
Outputs JSON: {"function": "rank_methods", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
SIZE = 100_000
10+
WARMUP = 5
11+
ITERATIONS = 30
12+
13+
# Data with many ties to stress different tie-breaking methods
14+
data = [float((i // 5) * 1.0) for i in range(SIZE)]
15+
s = pd.Series(data)
16+
17+
for _ in range(WARMUP):
18+
s.rank(method="min")
19+
s.rank(method="max")
20+
s.rank(method="first")
21+
s.rank(method="dense")
22+
23+
start = time.perf_counter()
24+
for _ in range(ITERATIONS):
25+
s.rank(method="min")
26+
s.rank(method="max")
27+
s.rank(method="first")
28+
s.rank(method="dense")
29+
total = (time.perf_counter() - start) * 1000
30+
31+
print(json.dumps({"function": "rank_methods", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total}))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Benchmark: dropnaDataFrame with advanced options (thresh, subset, axis=1).
3+
* Outputs JSON: {"function": "dropna_advanced", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { DataFrame, dropnaDataFrame } from "../../src/index.ts";
6+
7+
const SIZE = 10_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
// DataFrame with scattered null values
12+
const df = DataFrame.fromColumns({
13+
a: Array.from({ length: SIZE }, (_, i) => (i % 4 === 0 ? null : i * 0.1)),
14+
b: Array.from({ length: SIZE }, (_, i) => (i % 6 === 0 ? null : i * 2.0)),
15+
c: Array.from({ length: SIZE }, (_, i) => (i % 8 === 0 ? null : i % 100)),
16+
d: Array.from({ length: SIZE }, (_, i) => (i % 3 === 0 ? null : `val_${i % 20}`)),
17+
});
18+
19+
for (let i = 0; i < WARMUP; i++) {
20+
dropnaDataFrame(df, { thresh: 3 });
21+
dropnaDataFrame(df, { subset: ["a", "b"] });
22+
dropnaDataFrame(df, { axis: 1 });
23+
}
24+
25+
const start = performance.now();
26+
for (let i = 0; i < ITERATIONS; i++) {
27+
dropnaDataFrame(df, { thresh: 3 });
28+
dropnaDataFrame(df, { subset: ["a", "b"] });
29+
dropnaDataFrame(df, { axis: 1 });
30+
}
31+
const total = performance.now() - start;
32+
33+
console.log(JSON.stringify({ function: "dropna_advanced", mean_ms: total / ITERATIONS, iterations: ITERATIONS, total_ms: total }));
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Benchmark: factorize / seriesFactorize with sort=true and useNaSentinel options.
3+
* Outputs JSON: {"function": "factorize_sort", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { factorize, seriesFactorize, Series } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
const categories = ["zebra", "apple", "mango", "banana", "coconut", "date"];
12+
const data = Array.from({ length: SIZE }, (_, i) =>
13+
i % 15 === 0 ? null : categories[i % categories.length],
14+
);
15+
const s = new Series({ data });
16+
17+
for (let i = 0; i < WARMUP; i++) {
18+
factorize(data, { sort: true });
19+
factorize(data, { sort: true, useNaSentinel: true });
20+
seriesFactorize(s, { sort: true });
21+
}
22+
23+
const start = performance.now();
24+
for (let i = 0; i < ITERATIONS; i++) {
25+
factorize(data, { sort: true });
26+
factorize(data, { sort: true, useNaSentinel: true });
27+
seriesFactorize(s, { sort: true });
28+
}
29+
const total = performance.now() - start;
30+
31+
console.log(JSON.stringify({ function: "factorize_sort", mean_ms: total / ITERATIONS, iterations: ITERATIONS, total_ms: total }));
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Benchmark: getDummies / dataFrameGetDummies with prefix, dropFirst, dummyNa options.
3+
* Outputs JSON: {"function": "get_dummies_opts", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { getDummies, dataFrameGetDummies, Series, DataFrame } from "../../src/index.ts";
6+
7+
const SIZE = 10_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
const categories = ["apple", "banana", "cherry", "date", "elderberry"];
12+
const data = Array.from({ length: SIZE }, (_, i) =>
13+
i % 20 === 0 ? null : categories[i % categories.length],
14+
);
15+
const s = new Series({ data });
16+
17+
const df = DataFrame.fromColumns({
18+
fruit: Array.from({ length: SIZE }, (_, i) =>
19+
i % 20 === 0 ? null : categories[i % categories.length],
20+
),
21+
color: Array.from({ length: SIZE }, (_, i) => ["red", "green", "blue"][i % 3]),
22+
});
23+
24+
for (let i = 0; i < WARMUP; i++) {
25+
getDummies(s, { prefix: "cat", dummyNa: true });
26+
getDummies(s, { dropFirst: true });
27+
dataFrameGetDummies(df, { columns: ["fruit", "color"], prefix: "col", dropFirst: true });
28+
}
29+
30+
const start = performance.now();
31+
for (let i = 0; i < ITERATIONS; i++) {
32+
getDummies(s, { prefix: "cat", dummyNa: true });
33+
getDummies(s, { dropFirst: true });
34+
dataFrameGetDummies(df, { columns: ["fruit", "color"], prefix: "col", dropFirst: true });
35+
}
36+
const total = performance.now() - start;
37+
38+
console.log(JSON.stringify({ function: "get_dummies_opts", mean_ms: total / ITERATIONS, iterations: ITERATIONS, total_ms: total }));
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Benchmark: nancount / nanprod / nanmedian — extended nan-ignoring aggregates.
3+
* Outputs JSON: {"function": "nan_agg_extended", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { nancount, nanprod, nanmedian } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
// Array with ~15% NaN values
12+
const data: (number | null)[] = Array.from({ length: SIZE }, (_, i) =>
13+
i % 7 === 0 ? null : Math.cos(i * 0.02) * 50 + 1,
14+
);
15+
16+
for (let i = 0; i < WARMUP; i++) {
17+
nancount(data);
18+
nanprod(data.slice(0, 1000)); // nanprod on small slice to avoid overflow
19+
nanmedian(data);
20+
}
21+
22+
const start = performance.now();
23+
for (let i = 0; i < ITERATIONS; i++) {
24+
nancount(data);
25+
nanprod(data.slice(0, 1000));
26+
nanmedian(data);
27+
}
28+
const total = performance.now() - start;
29+
30+
console.log(JSON.stringify({ function: "nan_agg_extended", mean_ms: total / ITERATIONS, iterations: ITERATIONS, total_ms: total }));
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Benchmark: rankSeries with different tie-breaking methods (min/max/first/dense).
3+
* Outputs JSON: {"function": "rank_methods", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Series, rankSeries } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 30;
10+
11+
// Data with many ties to stress different tie-breaking methods
12+
const data = Array.from({ length: SIZE }, (_, i) => Math.floor(i / 5) * 1.0);
13+
const s = new Series({ data });
14+
15+
for (let i = 0; i < WARMUP; i++) {
16+
rankSeries(s, { method: "min" });
17+
rankSeries(s, { method: "max" });
18+
rankSeries(s, { method: "first" });
19+
rankSeries(s, { method: "dense" });
20+
}
21+
22+
const start = performance.now();
23+
for (let i = 0; i < ITERATIONS; i++) {
24+
rankSeries(s, { method: "min" });
25+
rankSeries(s, { method: "max" });
26+
rankSeries(s, { method: "first" });
27+
rankSeries(s, { method: "dense" });
28+
}
29+
const total = performance.now() - start;
30+
31+
console.log(JSON.stringify({ function: "rank_methods", mean_ms: total / ITERATIONS, iterations: ITERATIONS, total_ms: total }));

0 commit comments

Comments
 (0)