Skip to content

Commit be69e38

Browse files
Iteration 179: 5 new benchmark pairs (534→539, +5)
Added benchmarks for previously un-benchmarked functions: - numeric_stats_ext: percentileOfScore, minMaxNormalize, coefficientOfVariation - cat_ops_from_codes: catFromCodes, catSortByFreq, catToOrdinal - cat_ops_setops: catUnionCategories, catIntersectCategories, catDiffCategories - cat_freq_crosstab: catFreqTable, catCrossTab (with normalize) - natsort_ops: natCompare, natSorted, natArgSort Run: https://github.com/githubnext/tsessebe/actions/runs/24600207313 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5dd1667 commit be69e38

10 files changed

Lines changed: 439 additions & 0 deletions
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Benchmark: pd.Series.value_counts (freq table) and pd.crosstab for categorical data on 100k elements.
3+
Outputs JSON: {"function": "cat_freq_crosstab", "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 = 20
12+
13+
cats_a = ["alpha", "beta", "gamma", "delta", "epsilon"]
14+
cats_b = ["north", "south", "east", "west"]
15+
data_a = pd.Categorical([cats_a[i % len(cats_a)] for i in range(SIZE)], categories=cats_a)
16+
data_b = pd.Categorical([cats_b[i % len(cats_b)] for i in range(SIZE)], categories=cats_b)
17+
s_a = pd.Series(data_a)
18+
s_b = pd.Series(data_b)
19+
20+
for _ in range(WARMUP):
21+
s_a.value_counts(sort=False)
22+
pd.crosstab(s_a, s_b)
23+
pd.crosstab(s_a, s_b, normalize=True)
24+
25+
times = []
26+
for _ in range(ITERATIONS):
27+
t0 = time.perf_counter()
28+
s_a.value_counts(sort=False)
29+
pd.crosstab(s_a, s_b)
30+
pd.crosstab(s_a, s_b, normalize=True)
31+
times.append((time.perf_counter() - t0) * 1000)
32+
33+
total_ms = sum(times)
34+
print(json.dumps({
35+
"function": "cat_freq_crosstab",
36+
"mean_ms": total_ms / ITERATIONS,
37+
"iterations": ITERATIONS,
38+
"total_ms": total_ms,
39+
}))
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""
2+
Benchmark: pd.Categorical.from_codes, reorder_categories by freq, ordered categorical on 100k elements.
3+
Outputs JSON: {"function": "cat_ops_from_codes", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
import numpy as np
9+
10+
SIZE = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 20
13+
14+
categories = ["alpha", "beta", "gamma", "delta", "epsilon"]
15+
codes = [i % len(categories) for i in range(SIZE)]
16+
order = ["epsilon", "delta", "gamma", "beta", "alpha"]
17+
18+
def cat_from_codes():
19+
return pd.Categorical.from_codes(codes, categories=categories)
20+
21+
def cat_sort_by_freq(c):
22+
s = pd.Series(c)
23+
freq_order = s.value_counts().index.tolist()
24+
return s.astype(pd.CategoricalDtype(categories=freq_order, ordered=False))
25+
26+
def cat_to_ordinal(c):
27+
s = pd.Series(c)
28+
return s.astype(pd.CategoricalDtype(categories=order, ordered=True))
29+
30+
for _ in range(WARMUP):
31+
c = cat_from_codes()
32+
cat_sort_by_freq(c)
33+
cat_to_ordinal(c)
34+
35+
times = []
36+
for _ in range(ITERATIONS):
37+
t0 = time.perf_counter()
38+
c = cat_from_codes()
39+
cat_sort_by_freq(c)
40+
cat_to_ordinal(c)
41+
times.append((time.perf_counter() - t0) * 1000)
42+
43+
total_ms = sum(times)
44+
print(json.dumps({
45+
"function": "cat_ops_from_codes",
46+
"mean_ms": total_ms / ITERATIONS,
47+
"iterations": ITERATIONS,
48+
"total_ms": total_ms,
49+
}))
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Benchmark: categorical union/intersect/diff categories on 100k element Series.
3+
Outputs JSON: {"function": "cat_ops_setops", "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 = 20
12+
13+
cats_a = ["alpha", "beta", "gamma", "delta"]
14+
cats_b = ["gamma", "delta", "epsilon", "zeta"]
15+
data_a = [cats_a[i % len(cats_a)] for i in range(SIZE)]
16+
data_b = [cats_b[i % len(cats_b)] for i in range(SIZE)]
17+
s_a = pd.Series(data_a, dtype="category")
18+
s_b = pd.Series(data_b, dtype="category")
19+
20+
def cat_union(a, b):
21+
cats = list(dict.fromkeys(list(a.cat.categories) + [c for c in b.cat.categories if c not in a.cat.categories]))
22+
return a.astype(pd.CategoricalDtype(categories=cats))
23+
24+
def cat_intersect(a, b):
25+
cats = [c for c in a.cat.categories if c in set(b.cat.categories)]
26+
return a.astype(pd.CategoricalDtype(categories=cats))
27+
28+
def cat_diff(a, b):
29+
cats = [c for c in a.cat.categories if c not in set(b.cat.categories)]
30+
return a.astype(pd.CategoricalDtype(categories=cats))
31+
32+
for _ in range(WARMUP):
33+
cat_union(s_a, s_b)
34+
cat_intersect(s_a, s_b)
35+
cat_diff(s_a, s_b)
36+
37+
times = []
38+
for _ in range(ITERATIONS):
39+
t0 = time.perf_counter()
40+
cat_union(s_a, s_b)
41+
cat_intersect(s_a, s_b)
42+
cat_diff(s_a, s_b)
43+
times.append((time.perf_counter() - t0) * 1000)
44+
45+
total_ms = sum(times)
46+
print(json.dumps({
47+
"function": "cat_ops_setops",
48+
"mean_ms": total_ms / ITERATIONS,
49+
"iterations": ITERATIONS,
50+
"total_ms": total_ms,
51+
}))
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
Benchmark: natsort.natsorted and natsort.index_natsorted on filename-like strings.
3+
Outputs JSON: {"function": "natsort_ops", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
8+
SIZE = 10_000
9+
WARMUP = 5
10+
ITERATIONS = 20
11+
12+
filenames = [f"file{i % 100}_chunk{i // 100}.txt" for i in range(SIZE)]
13+
14+
def nat_compare(a, b):
15+
"""Natural comparison: return -1/0/1 by tokenizing digit runs."""
16+
import re
17+
def tokenize(s):
18+
parts = re.split(r'(\d+)', s)
19+
return [int(p) if p.isdigit() else p for p in parts]
20+
ta, tb = tokenize(a), tokenize(b)
21+
return (ta > tb) - (ta < tb)
22+
23+
def nat_sorted(arr):
24+
import re
25+
def key(s):
26+
parts = re.split(r'(\d+)', s)
27+
return [int(p) if p.isdigit() else p for p in parts]
28+
return sorted(arr, key=key)
29+
30+
def nat_argsort(arr):
31+
import re
32+
def key(s):
33+
parts = re.split(r'(\d+)', s)
34+
return [int(p) if p.isdigit() else p for p in parts]
35+
return [i for i, _ in sorted(enumerate(arr), key=lambda x: key(x[1]))]
36+
37+
for _ in range(WARMUP):
38+
nat_compare("file10.txt", "file9.txt")
39+
nat_sorted(filenames)
40+
nat_argsort(filenames)
41+
42+
times = []
43+
for _ in range(ITERATIONS):
44+
t0 = time.perf_counter()
45+
nat_compare("file10.txt", "file9.txt")
46+
nat_sorted(filenames)
47+
nat_argsort(filenames)
48+
times.append((time.perf_counter() - t0) * 1000)
49+
50+
total_ms = sum(times)
51+
print(json.dumps({
52+
"function": "natsort_ops",
53+
"mean_ms": total_ms / ITERATIONS,
54+
"iterations": ITERATIONS,
55+
"total_ms": total_ms,
56+
}))
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Benchmark: scipy percentileofscore, min-max normalization, coefficient of variation on 100k elements.
3+
Outputs JSON: {"function": "numeric_stats_ext", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import math
8+
import pandas as pd
9+
import numpy as np
10+
11+
SIZE = 100_000
12+
WARMUP = 5
13+
ITERATIONS = 20
14+
15+
data = [math.sin(i * 0.001) * 100 + 50 for i in range(SIZE)]
16+
s = pd.Series(data)
17+
18+
def percentile_of_score(arr, score):
19+
"""Compute percentile rank of score (rank method)."""
20+
n = len(arr)
21+
below = sum(1 for v in arr if v < score)
22+
equal = sum(1 for v in arr if v == score)
23+
return (below + 0.5 * equal) / n * 100
24+
25+
def min_max_normalize(series):
26+
mn, mx = series.min(), series.max()
27+
return (series - mn) / (mx - mn)
28+
29+
def coeff_of_variation(series):
30+
return series.std(ddof=1) / series.mean()
31+
32+
for _ in range(WARMUP):
33+
percentile_of_score(data, 50)
34+
min_max_normalize(s)
35+
coeff_of_variation(s)
36+
37+
times = []
38+
for _ in range(ITERATIONS):
39+
t0 = time.perf_counter()
40+
percentile_of_score(data, 50)
41+
min_max_normalize(s)
42+
coeff_of_variation(s)
43+
times.append((time.perf_counter() - t0) * 1000)
44+
45+
total_ms = sum(times)
46+
print(json.dumps({
47+
"function": "numeric_stats_ext",
48+
"mean_ms": total_ms / ITERATIONS,
49+
"iterations": ITERATIONS,
50+
"total_ms": total_ms,
51+
}))
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Benchmark: catFreqTable and catCrossTab on 100k elements.
3+
* Outputs JSON: {"function": "cat_freq_crosstab", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { catFromCodes, catFreqTable, catCrossTab } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 20;
10+
11+
const catsA = ["alpha", "beta", "gamma", "delta", "epsilon"];
12+
const catsB = ["north", "south", "east", "west"];
13+
const codesA = Array.from({ length: SIZE }, (_, i) => i % catsA.length);
14+
const codesB = Array.from({ length: SIZE }, (_, i) => i % catsB.length);
15+
const csA = catFromCodes(codesA, catsA);
16+
const csB = catFromCodes(codesB, catsB);
17+
18+
for (let i = 0; i < WARMUP; i++) {
19+
catFreqTable(csA);
20+
catCrossTab(csA, csB);
21+
catCrossTab(csA, csB, { normalize: true });
22+
}
23+
24+
const times: number[] = [];
25+
for (let i = 0; i < ITERATIONS; i++) {
26+
const t0 = performance.now();
27+
catFreqTable(csA);
28+
catCrossTab(csA, csB);
29+
catCrossTab(csA, csB, { normalize: true });
30+
times.push(performance.now() - t0);
31+
}
32+
33+
const total = times.reduce((a, b) => a + b, 0);
34+
console.log(
35+
JSON.stringify({
36+
function: "cat_freq_crosstab",
37+
mean_ms: total / ITERATIONS,
38+
iterations: ITERATIONS,
39+
total_ms: total,
40+
}),
41+
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Benchmark: catFromCodes, catSortByFreq, catToOrdinal on 100k elements.
3+
* Outputs JSON: {"function": "cat_ops_from_codes", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { catFromCodes, catSortByFreq, catToOrdinal } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 20;
10+
11+
const categories = ["alpha", "beta", "gamma", "delta", "epsilon"];
12+
const codes = Array.from({ length: SIZE }, (_, i) => i % categories.length);
13+
const order = ["epsilon", "delta", "gamma", "beta", "alpha"];
14+
15+
for (let i = 0; i < WARMUP; i++) {
16+
const cs = catFromCodes(codes, categories);
17+
catSortByFreq(cs);
18+
catToOrdinal(cs, order);
19+
}
20+
21+
const times: number[] = [];
22+
for (let i = 0; i < ITERATIONS; i++) {
23+
const t0 = performance.now();
24+
const cs = catFromCodes(codes, categories);
25+
catSortByFreq(cs);
26+
catToOrdinal(cs, order);
27+
times.push(performance.now() - t0);
28+
}
29+
30+
const total = times.reduce((a, b) => a + b, 0);
31+
console.log(
32+
JSON.stringify({
33+
function: "cat_ops_from_codes",
34+
mean_ms: total / ITERATIONS,
35+
iterations: ITERATIONS,
36+
total_ms: total,
37+
}),
38+
);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Benchmark: catUnionCategories, catIntersectCategories, catDiffCategories on 100k elements.
3+
* Outputs JSON: {"function": "cat_ops_setops", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { catFromCodes, catUnionCategories, catIntersectCategories, catDiffCategories } from "../../src/index.ts";
6+
7+
const SIZE = 100_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 20;
10+
11+
const catsA = ["alpha", "beta", "gamma", "delta"];
12+
const catsB = ["gamma", "delta", "epsilon", "zeta"];
13+
const codesA = Array.from({ length: SIZE }, (_, i) => i % catsA.length);
14+
const codesB = Array.from({ length: SIZE }, (_, i) => i % catsB.length);
15+
const csA = catFromCodes(codesA, catsA);
16+
const csB = catFromCodes(codesB, catsB);
17+
18+
for (let i = 0; i < WARMUP; i++) {
19+
catUnionCategories(csA, csB);
20+
catIntersectCategories(csA, csB);
21+
catDiffCategories(csA, csB);
22+
}
23+
24+
const times: number[] = [];
25+
for (let i = 0; i < ITERATIONS; i++) {
26+
const t0 = performance.now();
27+
catUnionCategories(csA, csB);
28+
catIntersectCategories(csA, csB);
29+
catDiffCategories(csA, csB);
30+
times.push(performance.now() - t0);
31+
}
32+
33+
const total = times.reduce((a, b) => a + b, 0);
34+
console.log(
35+
JSON.stringify({
36+
function: "cat_ops_setops",
37+
mean_ms: total / ITERATIONS,
38+
iterations: ITERATIONS,
39+
total_ms: total,
40+
}),
41+
);

0 commit comments

Comments
 (0)