Skip to content

Commit 92932ac

Browse files
authored
Merge pull request #423 from githubnext/autoloop/perf-comparison
[Autoloop: perf-comparison] Iteration 392: gaussianKDE benchmark pair
2 parents e1c8cf1 + 3332450 commit 92932ac

54 files changed

Lines changed: 2756 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
Benchmark: Business and Quarter date offsets — QuarterEnd, QuarterBegin,
3+
BMonthEnd, BMonthBegin, BYearEnd, BYearBegin.
4+
Mirrors tsb bench_business_offsets.ts.
5+
Dataset: 5,000 dates; 50 measured iterations.
6+
Outputs JSON: {"function": "business_offsets", "mean_ms": ..., "iterations": ..., "total_ms": ...}
7+
"""
8+
import json
9+
import time
10+
from datetime import datetime, timedelta, timezone
11+
12+
import pandas as pd
13+
from pandas.tseries.offsets import (
14+
BMonthBegin,
15+
BMonthEnd,
16+
BYearBegin,
17+
BYearEnd,
18+
QuarterBegin,
19+
QuarterEnd,
20+
)
21+
22+
SIZE = 5_000
23+
WARMUP = 5
24+
ITERATIONS = 50
25+
26+
q_end = QuarterEnd(1)
27+
q_begin = QuarterBegin(1)
28+
bm_end = BMonthEnd(1)
29+
bm_begin = BMonthBegin(1)
30+
by_end = BYearEnd(1)
31+
by_begin = BYearBegin(1)
32+
33+
base = datetime(2020, 1, 15, tzinfo=timezone.utc)
34+
dates = [base + timedelta(days=i) for i in range(SIZE)]
35+
ts_dates = [pd.Timestamp(d) for d in dates]
36+
37+
for _ in range(WARMUP):
38+
for d in ts_dates[:100]:
39+
d + q_end
40+
d + q_begin
41+
d + bm_end
42+
d + bm_begin
43+
d + by_end
44+
d + by_begin
45+
46+
t0 = time.perf_counter()
47+
for _ in range(ITERATIONS):
48+
for d in ts_dates:
49+
d + q_end
50+
d + q_begin
51+
d + bm_end
52+
d + bm_begin
53+
d + by_end
54+
d + by_begin
55+
total_ms = (time.perf_counter() - t0) * 1000
56+
mean_ms = total_ms / ITERATIONS
57+
58+
print(json.dumps({"function": "business_offsets", "mean_ms": mean_ms, "iterations": ITERATIONS, "total_ms": total_ms}))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Benchmark: case_when — conditional value selection on 100k-element Series (pandas 2.2+)"""
2+
import json, time
3+
import numpy as np
4+
import pandas as pd
5+
6+
ROWS = 100_000
7+
WARMUP = 5
8+
ITERATIONS = 20
9+
10+
data = np.arange(ROWS, dtype=float) % 100
11+
s = pd.Series(data)
12+
cond1 = s < 25
13+
cond2 = s < 50
14+
cond3 = s < 75
15+
16+
caselist = [
17+
(cond1, "low"),
18+
(cond2, "medium-low"),
19+
(cond3, "medium-high"),
20+
]
21+
22+
for _ in range(WARMUP):
23+
s.case_when(caselist)
24+
25+
start = time.perf_counter()
26+
for _ in range(ITERATIONS):
27+
s.case_when(caselist)
28+
total = (time.perf_counter() - start) * 1000
29+
30+
print(json.dumps({
31+
"function": "case_when",
32+
"mean_ms": total / ITERATIONS,
33+
"iterations": ITERATIONS,
34+
"total_ms": total,
35+
}))
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""Benchmark: categorical operations on 100k-element Series
2+
3+
Covers pd.Categorical.from_codes, value_counts (sort by freq), and pd.crosstab.
4+
"""
5+
import json
6+
import time
7+
import numpy as np
8+
import pandas as pd
9+
10+
ROWS = 100_000
11+
WARMUP = 3
12+
ITERATIONS = 10
13+
14+
# Build a categorical Series from codes + categories
15+
CATEGORIES = ["alpha", "beta", "gamma", "delta", "epsilon"]
16+
codes = np.arange(ROWS) % len(CATEGORIES)
17+
cat_series = pd.Series(pd.Categorical.from_codes(codes, categories=CATEGORIES))
18+
19+
# Build a second categorical for crosstab
20+
CATEGORIES2 = ["x", "y", "z"]
21+
codes2 = np.arange(ROWS) % len(CATEGORIES2)
22+
cat_series2 = pd.Series(pd.Categorical.from_codes(codes2, categories=CATEGORIES2))
23+
24+
# Warm up
25+
for _ in range(WARMUP):
26+
cat_series.value_counts()
27+
cat_series.value_counts(sort=True)
28+
pd.crosstab(cat_series, cat_series2)
29+
30+
# Measure value_counts (analogous to catFreqTable)
31+
start = time.perf_counter()
32+
for _ in range(ITERATIONS):
33+
cat_series.value_counts(sort=False)
34+
freq_table_ms = (time.perf_counter() - start) * 1000 / ITERATIONS
35+
36+
# Measure value_counts sorted by freq (analogous to catSortByFreq)
37+
start = time.perf_counter()
38+
for _ in range(ITERATIONS):
39+
cat_series.value_counts(sort=True)
40+
sort_by_freq_ms = (time.perf_counter() - start) * 1000 / ITERATIONS
41+
42+
# Measure crosstab (analogous to catCrossTab)
43+
start = time.perf_counter()
44+
for _ in range(ITERATIONS):
45+
pd.crosstab(cat_series, cat_series2)
46+
cross_tab_ms = (time.perf_counter() - start) * 1000 / ITERATIONS
47+
48+
mean_ms = (freq_table_ms + sort_by_freq_ms + cross_tab_ms) / 3
49+
50+
print(json.dumps({
51+
"function": "categorical_ops",
52+
"mean_ms": mean_ms,
53+
"iterations": ITERATIONS,
54+
"total_ms": mean_ms * ITERATIONS,
55+
"details": {
56+
"freqTableMs": freq_table_ms,
57+
"sortByFreqMs": sort_by_freq_ms,
58+
"crossTabMs": cross_tab_ms,
59+
},
60+
}))
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Benchmark: contingency — expected_freq, relative_risk, odds_ratio, association
3+
Pure-numpy equivalents (no scipy) matching the TypeScript benchmark.
4+
Dataset: same 4x4 table as the TypeScript benchmark.
5+
"""
6+
import json
7+
import time
8+
import numpy as np
9+
10+
WARMUP = 10
11+
ITERS = 50
12+
13+
observed = np.array([
14+
[120, 80, 40, 60],
15+
[90, 110, 70, 30],
16+
[50, 60, 100, 90],
17+
[40, 50, 90, 120],
18+
], dtype=float)
19+
20+
two_by_two = np.array([
21+
[60.0, 40.0],
22+
[30.0, 70.0],
23+
])
24+
25+
26+
def expected_freq(obs):
27+
row_sums = obs.sum(axis=1, keepdims=True)
28+
col_sums = obs.sum(axis=0, keepdims=True)
29+
total = obs.sum()
30+
return row_sums * col_sums / total
31+
32+
33+
def relative_risk(obs):
34+
a, b = obs[0, 0], obs[0, 1]
35+
c, d = obs[1, 0], obs[1, 1]
36+
risk0 = a / (a + b)
37+
risk1 = c / (c + d)
38+
return risk0 / risk1
39+
40+
41+
def odds_ratio(obs):
42+
a, b = obs[0, 0], obs[0, 1]
43+
c, d = obs[1, 0], obs[1, 1]
44+
return (a * d) / (b * c)
45+
46+
47+
def association_cramer(obs):
48+
exp = expected_freq(obs)
49+
chi2 = np.sum((obs - exp) ** 2 / exp)
50+
n = obs.sum()
51+
r, c = obs.shape
52+
return np.sqrt(chi2 / n / min(r - 1, c - 1))
53+
54+
55+
# Warm up
56+
for _ in range(WARMUP):
57+
expected_freq(observed)
58+
relative_risk(two_by_two)
59+
odds_ratio(two_by_two)
60+
association_cramer(observed)
61+
62+
# Measure
63+
start = time.perf_counter()
64+
for _ in range(ITERS):
65+
expected_freq(observed)
66+
relative_risk(two_by_two)
67+
odds_ratio(two_by_two)
68+
association_cramer(observed)
69+
total_s = time.perf_counter() - start
70+
total_ms = total_s * 1000
71+
mean_ms = total_ms / ITERS
72+
73+
print(json.dumps({
74+
"function": "contingency",
75+
"mean_ms": round(mean_ms, 4),
76+
"iterations": ITERS,
77+
"total_ms": round(total_ms, 4),
78+
}))

benchmarks/pandas/bench_entropy.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import numpy as np
2+
import json
3+
import time
4+
5+
N = 100
6+
WARMUP = 5
7+
ITERS = 50
8+
9+
p = np.arange(1, N + 1, dtype=float)
10+
q = np.arange(N, 0, -1, dtype=float)
11+
12+
# Normalise
13+
p_norm = p / p.sum()
14+
q_norm = q / q.sum()
15+
16+
17+
def entropy_fn(pk):
18+
pk = pk / pk.sum()
19+
return -np.sum(pk * np.log(pk + 1e-300))
20+
21+
22+
def kl_divergence(pk, qk):
23+
pk = pk / pk.sum()
24+
qk = qk / qk.sum()
25+
mask = pk > 0
26+
return np.sum(pk[mask] * np.log(pk[mask] / (qk[mask] + 1e-300)))
27+
28+
29+
for _ in range(WARMUP):
30+
entropy_fn(p)
31+
kl_divergence(p, q)
32+
33+
t0 = time.perf_counter()
34+
for _ in range(ITERS):
35+
entropy_fn(p)
36+
kl_divergence(p, q)
37+
total_ms = (time.perf_counter() - t0) * 1000
38+
39+
print(json.dumps({
40+
"function": "entropy_klDivergence",
41+
"mean_ms": total_ms / ITERS,
42+
"iterations": ITERS,
43+
"total_ms": total_ms,
44+
}))

benchmarks/pandas/bench_feather.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
Benchmark: read_feather / to_feather — Arrow Feather v2 round-trip on 10k rows
3+
"""
4+
import json
5+
import time
6+
import io
7+
import pandas as pd
8+
import numpy as np
9+
10+
ROWS = 10_000
11+
WARMUP = 3
12+
ITERATIONS = 20
13+
14+
rng = np.random.default_rng(42)
15+
df = pd.DataFrame({
16+
"id": np.arange(ROWS, dtype=np.int64),
17+
"value": np.arange(ROWS, dtype=np.float64) * 1.1,
18+
"label": [f"cat_{i % 50}" for i in range(ROWS)],
19+
})
20+
21+
# Warm up
22+
for _ in range(WARMUP):
23+
buf = io.BytesIO()
24+
df.to_feather(buf)
25+
buf.seek(0)
26+
pd.read_feather(buf)
27+
28+
# Measure round-trip
29+
start = time.perf_counter()
30+
for _ in range(ITERATIONS):
31+
buf = io.BytesIO()
32+
df.to_feather(buf)
33+
buf.seek(0)
34+
pd.read_feather(buf)
35+
total = (time.perf_counter() - start) * 1000
36+
37+
print(json.dumps({
38+
"function": "feather",
39+
"mean_ms": total / ITERATIONS,
40+
"iterations": ITERATIONS,
41+
"total_ms": total,
42+
}))
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""
2+
Benchmark: flags and options (pandas equivalent)
3+
4+
Measures:
5+
- DataFrame.flags.allows_duplicate_labels get+set
6+
- pd.get_option / pd.set_option / pd.reset_option for multiple keys
7+
- pd.options proxy read
8+
9+
Dataset: 10,000-row Series and DataFrame; 20 measured iterations.
10+
"""
11+
12+
import json
13+
import time
14+
15+
import numpy as np
16+
import pandas as pd
17+
18+
N = 10_000
19+
WARMUP = 5
20+
ITERS = 20
21+
22+
data = np.arange(N, dtype=np.float64)
23+
s = pd.Series(data)
24+
df = pd.DataFrame({"a": data, "b": data})
25+
26+
27+
def bench_flags_options():
28+
sink = 0
29+
for _ in range(ITERS + WARMUP):
30+
# flags on Series
31+
prev = s.flags.allows_duplicate_labels
32+
s.flags.allows_duplicate_labels = not prev
33+
s.flags.allows_duplicate_labels = prev
34+
sink ^= int(s.flags.allows_duplicate_labels)
35+
36+
# flags on DataFrame
37+
prev_df = df.flags.allows_duplicate_labels
38+
df.flags.allows_duplicate_labels = not prev_df
39+
df.flags.allows_duplicate_labels = prev_df
40+
sink ^= int(df.flags.allows_duplicate_labels)
41+
42+
# options get/set/reset
43+
v = pd.get_option("display.max_rows")
44+
pd.set_option("display.max_rows", v + 1)
45+
pd.reset_option("display.max_rows")
46+
sink ^= int(pd.options.display.max_rows > 0)
47+
48+
pd.set_option("display.max_columns", 20)
49+
pd.reset_option("display.max_columns")
50+
sink ^= int(pd.options.display.max_columns > 0)
51+
52+
return sink
53+
54+
55+
# Warm-up
56+
bench_flags_options()
57+
58+
# Measure
59+
t0 = time.perf_counter()
60+
for _ in range(ITERS):
61+
bench_flags_options()
62+
total = (time.perf_counter() - t0) * 1000 # ms
63+
64+
print(
65+
json.dumps(
66+
{
67+
"function": "flags_options",
68+
"mean_ms": total / ITERS,
69+
"iterations": ITERS,
70+
"total_ms": total,
71+
}
72+
)
73+
)

0 commit comments

Comments
 (0)