Skip to content

Commit 8def030

Browse files
authored
Merge pull request #150 from githubnext/autoloop/perf-comparison-8724e9f9
[Autoloop] [Autoloop: perf-comparison]
2 parents da7e31e + 36dd643 commit 8def030

130 files changed

Lines changed: 4917 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

benchmarks/pandas/bench_any_all.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Benchmark: any_all — Series.any / all and DataFrame.any / all on 100k rows."""
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.arange(SIZE) % 2 == 0)
11+
df = pd.DataFrame({
12+
"a": np.arange(SIZE) % 3 != 0,
13+
"b": np.arange(SIZE) > 0,
14+
"c": np.ones(SIZE, dtype=bool),
15+
})
16+
17+
for _ in range(WARMUP):
18+
s.any()
19+
s.all()
20+
df.any()
21+
df.all()
22+
23+
start = time.perf_counter()
24+
for _ in range(ITERATIONS):
25+
s.any()
26+
s.all()
27+
df.any()
28+
df.all()
29+
total = (time.perf_counter() - start) * 1000
30+
31+
print(json.dumps({
32+
"function": "any_all",
33+
"mean_ms": total / ITERATIONS,
34+
"iterations": ITERATIONS,
35+
"total_ms": total,
36+
}))
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Benchmark: astype standalone — DataFrame.astype with per-column and uniform dtype on 100k-row DataFrame."""
2+
import json, time
3+
import pandas as pd
4+
import numpy as np
5+
6+
SIZE = 100_000
7+
WARMUP = 5
8+
ITERATIONS = 50
9+
10+
df = pd.DataFrame({
11+
"a": np.arange(SIZE, dtype=np.float64),
12+
"b": np.arange(SIZE, dtype=np.int64),
13+
"c": np.where(np.arange(SIZE) % 2 == 0, 1, 0).astype(np.int64),
14+
})
15+
16+
for _ in range(WARMUP):
17+
df.astype({"a": "float32", "b": "int32"})
18+
df.astype("float64")
19+
20+
start = time.perf_counter()
21+
for _ in range(ITERATIONS):
22+
df.astype({"a": "float32", "b": "int32"})
23+
df.astype("float64")
24+
total = (time.perf_counter() - start) * 1000
25+
26+
print(json.dumps({
27+
"function": "astype_df_fn",
28+
"mean_ms": round(total / ITERATIONS, 3),
29+
"iterations": ITERATIONS,
30+
"total_ms": round(total, 3),
31+
}))
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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Benchmark: pandas category set operations — intersection and difference of
3+
categorical Series categories (100k-element, 20 categories each).
4+
Mirrors tsb's catIntersectCategories / catDiffCategories.
5+
Outputs JSON: {"function": "cat_intersect_diff", "mean_ms": ..., "iterations": ..., "total_ms": ...}
6+
"""
7+
import json
8+
import time
9+
import pandas as pd
10+
11+
SIZE = 100_000
12+
WARMUP = 5
13+
ITERATIONS = 30
14+
15+
cats_a = [f"cat_a_{i}" for i in range(20)]
16+
cats_b = [f"cat_{'a' if i < 10 else 'b'}_{i}" for i in range(20)]
17+
18+
data_a = [cats_a[i % len(cats_a)] for i in range(SIZE)]
19+
data_b = [cats_b[i % len(cats_b)] for i in range(SIZE)]
20+
21+
s_a = pd.Categorical(data_a, categories=cats_a)
22+
s_b = pd.Categorical(data_b, categories=cats_b)
23+
24+
def cat_intersect(a, b):
25+
"""Return new Categorical with categories = intersection of a.categories and b.categories."""
26+
b_set = set(b.categories)
27+
intersected = [c for c in a.categories if c in b_set]
28+
return pd.Categorical(a, categories=intersected)
29+
30+
def cat_diff(a, b):
31+
"""Return new Categorical with categories = a.categories - b.categories."""
32+
b_set = set(b.categories)
33+
remaining = [c for c in a.categories if c not in b_set]
34+
return pd.Categorical(a, categories=remaining)
35+
36+
for _ in range(WARMUP):
37+
cat_intersect(s_a, s_b)
38+
cat_diff(s_a, s_b)
39+
40+
times = []
41+
for _ in range(ITERATIONS):
42+
t0 = time.perf_counter()
43+
cat_intersect(s_a, s_b)
44+
cat_diff(s_a, s_b)
45+
times.append((time.perf_counter() - t0) * 1000)
46+
47+
total_ms = sum(times)
48+
print(json.dumps({
49+
"function": "cat_intersect_diff",
50+
"mean_ms": total_ms / ITERATIONS,
51+
"iterations": ITERATIONS,
52+
"total_ms": total_ms,
53+
}))
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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Benchmark: combineFirstSeries standalone — pd.Series.combine_first() on 50k-element Series with 30% NaN."""
2+
import json, time
3+
import pandas as pd
4+
import numpy as np
5+
6+
SIZE = 50_000
7+
WARMUP = 5
8+
ITERATIONS = 30
9+
10+
rng = np.random.default_rng(42)
11+
data1 = rng.standard_normal(SIZE)
12+
data1[::3] = float("nan") # ~30% nulls
13+
s1 = pd.Series(data1)
14+
s2 = pd.Series(np.arange(SIZE, dtype=np.float64) * 2.0)
15+
16+
for _ in range(WARMUP):
17+
s1.combine_first(s2)
18+
19+
start = time.perf_counter()
20+
for _ in range(ITERATIONS):
21+
s1.combine_first(s2)
22+
total = (time.perf_counter() - start) * 1000
23+
24+
print(json.dumps({
25+
"function": "combine_first_fn",
26+
"mean_ms": round(total / ITERATIONS, 3),
27+
"iterations": ITERATIONS,
28+
"total_ms": round(total, 3),
29+
}))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Benchmark: Series.combine_first (standalone equivalent) — fill missing values from another Series.
2+
Mirrors tsb bench_combine_first_series.ts for pandas.
3+
"""
4+
import json, time
5+
import pandas as pd
6+
import numpy as np
7+
8+
SIZE = 10_000
9+
WARMUP = 5
10+
ITERATIONS = 50
11+
12+
data1 = [None if i % 3 == 0 else i * 0.5 for i in range(SIZE)]
13+
data2 = [i * 0.1 for i in range(SIZE)]
14+
s1 = pd.Series(data1)
15+
s2 = pd.Series(data2)
16+
17+
for _ in range(WARMUP):
18+
s1.combine_first(s2)
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
s1.combine_first(s2)
24+
times.append((time.perf_counter() - t0) * 1000)
25+
26+
total = sum(times)
27+
mean = total / ITERATIONS
28+
print(json.dumps({
29+
"function": "combine_first_series",
30+
"mean_ms": round(mean, 3),
31+
"iterations": ITERATIONS,
32+
"total_ms": round(total, 3),
33+
}))
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
Benchmark: Series.combine_first() — fill NaN values from another Series (union of indexes).
3+
Mirrors tsb bench_combine_first_series_fn.ts (standalone combineFirstSeries fn).
4+
Outputs JSON: {"function": "combine_first_series_fn", "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 = 30
14+
15+
rng = np.random.default_rng(42)
16+
raw = rng.uniform(0, 10, SIZE)
17+
mask = rng.integers(0, 4, SIZE) == 0 # ~25% nulls
18+
d1 = pd.array(raw, dtype="Float64")
19+
for idx in range(SIZE):
20+
if mask[idx]:
21+
d1[idx] = pd.NA
22+
23+
s1 = pd.Series(d1, dtype="Float64")
24+
s2 = pd.Series(rng.uniform(0, 10, SIZE))
25+
26+
for _ in range(WARMUP):
27+
s1.combine_first(s2)
28+
29+
times = []
30+
for _ in range(ITERATIONS):
31+
t0 = time.perf_counter()
32+
s1.combine_first(s2)
33+
times.append((time.perf_counter() - t0) * 1000)
34+
35+
total_ms = sum(times)
36+
print(json.dumps({
37+
"function": "combine_first_series_fn",
38+
"mean_ms": round(total_ms / ITERATIONS, 3),
39+
"iterations": ITERATIONS,
40+
"total_ms": round(total_ms, 3),
41+
}))

0 commit comments

Comments
 (0)