Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4397e30
[Autoloop: perf-comparison] Iteration 482: add wasm_agg_ops benchmark…
github-actions[bot] Aug 24, 2026
bb3aa91
ci: trigger checks
github-actions[bot] Aug 24, 2026
1197660
[Autoloop: perf-comparison] Iteration 483: add wasm_rolling_stats ben…
github-actions[bot] Aug 25, 2026
410d3b8
ci: trigger checks
github-actions[bot] Aug 25, 2026
6808d33
[Autoloop: perf-comparison] Iteration 484: add to_dict_series_orient …
github-actions[bot] Aug 25, 2026
25ce6e8
ci: trigger checks
github-actions[bot] Aug 25, 2026
c090288
[Autoloop: perf-comparison] Iteration 485: add registerOption benchma…
github-actions[bot] Aug 26, 2026
45cb293
ci: trigger checks
github-actions[bot] Aug 26, 2026
78ffce6
perf: add MultiIndex.toList() benchmark pair
github-actions[bot] Aug 27, 2026
5090240
ci: trigger checks
github-actions[bot] Aug 27, 2026
fceac8c
[Autoloop: perf-comparison] Iteration 487: add string_array_str_ops b…
github-actions[bot] Aug 27, 2026
011cb3b
ci: trigger checks
github-actions[bot] Aug 27, 2026
56519d0
[Autoloop: perf-comparison] Iteration 488: Add ewm benchmark
github-actions[bot] Aug 28, 2026
49578de
ci: trigger checks
github-actions[bot] Aug 28, 2026
85dc945
[Autoloop: perf-comparison] Iteration 489: add string_array_cat bench…
github-actions[bot] Aug 28, 2026
c1f5164
ci: trigger checks
github-actions[bot] Aug 28, 2026
fd0704a
[Autoloop: perf-comparison] Iteration 490: add series_rename_ops benc…
github-actions[bot] Aug 29, 2026
3d3bdcb
ci: trigger checks
github-actions[bot] Aug 29, 2026
5d7effd
[Autoloop: perf-comparison] Iteration 491: add wasm_rolling_sum_mean …
github-actions[bot] Aug 29, 2026
ccb4fca
ci: trigger checks
github-actions[bot] Aug 29, 2026
dce1128
[Autoloop: perf-comparison] Iteration 492: add datetime_index_min_max…
github-actions[bot] Aug 30, 2026
59b9e38
ci: trigger checks
github-actions[bot] Aug 30, 2026
bbd59e8
[Autoloop: perf-comparison] Iteration 493: add sparse_array_advanced …
github-actions[bot] Aug 30, 2026
46d1ff2
ci: trigger checks
github-actions[bot] Aug 30, 2026
9855436
[Autoloop: perf-comparison] Iteration 494: add wasm_expanding_stats b…
github-actions[bot] Aug 30, 2026
c070e2f
ci: trigger checks
github-actions[bot] Aug 30, 2026
85b793c
[Autoloop: perf-comparison] Iteration 495: Add stack/unstack benchmark
github-actions[bot] Aug 31, 2026
180ef68
ci: trigger checks
github-actions[bot] Aug 31, 2026
bd27092
[Autoloop: perf-comparison] Iteration 496: bench seriesDigitize and c…
github-actions[bot] Aug 31, 2026
bae8fc7
ci: trigger checks
github-actions[bot] Aug 31, 2026
ef191e6
[Autoloop: perf-comparison] Iteration 497: Add CategoricalAccessor mu…
github-actions[bot] Sep 1, 2026
d6a2ece
ci: trigger checks
github-actions[bot] Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions benchmarks/pandas/bench_cat_accessor_mutation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import json
import time
import pandas as pd

N = 50_000
CATS = ["alpha", "beta", "gamma", "delta", "epsilon"]
data = [CATS[i % len(CATS)] for i in range(N)]
series = pd.Series(pd.Categorical(data, categories=CATS))

WARMUP = 5
ITERATIONS = 50


def run():
# remove_categories — remove an absent category (safe no-op)
series.cat.remove_categories([])

# rename_categories — rename via dict
series.cat.rename_categories(
{"alpha": "a", "beta": "b", "gamma": "c", "delta": "d", "epsilon": "e"}
)

# set_categories — replace with a superset
series.cat.set_categories(
["alpha", "beta", "gamma", "delta", "epsilon", "zeta"], ordered=False
)

# reorder_categories — same set, different order
series.cat.reorder_categories(["epsilon", "delta", "gamma", "beta", "alpha"])

# as_ordered / as_unordered — flip ordered flag
series.cat.as_ordered()
series.cat.as_unordered()


for _ in range(WARMUP):
run()

start = time.perf_counter()
for _ in range(ITERATIONS):
run()
total_ms = (time.perf_counter() - start) * 1000

print(
json.dumps(
{
"function": "cat_accessor_mutation",
"mean_ms": total_ms / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total_ms,
}
)
)
45 changes: 45 additions & 0 deletions benchmarks/pandas/bench_datetime_index_min_max.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
Benchmark: pandas DatetimeIndex — min(), max(), index access, to_pydatetime(), asi8
on a 10,000-element DatetimeIndex.

Mirrors tsb bench_datetime_index_min_max.ts.

Outputs JSON: {"function": "datetime_index_min_max", "mean_ms": ..., "iterations": ..., "total_ms": ...}
"""
import json
import time
import pandas as pd

SIZE = 10_000
WARMUP = 5
ITERATIONS = 50

idx = pd.date_range(start="2000-01-01", periods=SIZE, freq="h")
mid = SIZE // 2

# Warm-up
for _ in range(WARMUP):
idx.min()
idx.max()
_ = idx[mid]
idx.to_pydatetime()
idx.asi8

start = time.perf_counter()
for _ in range(ITERATIONS):
idx.min()
idx.max()
_ = idx[mid]
idx.to_pydatetime()
idx.asi8
total_s = time.perf_counter() - start

total_ms = total_s * 1000
mean_ms = total_ms / ITERATIONS

print(json.dumps({
"function": "datetime_index_min_max",
"mean_ms": mean_ms,
"iterations": ITERATIONS,
"total_ms": total_ms,
}))
23 changes: 23 additions & 0 deletions benchmarks/pandas/bench_ewm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Benchmark: ewm (Exponentially Weighted Moving) aggregations on 100k-element pandas Series"""
import json, time, math
import numpy as np
import pandas as pd

ROWS = 100_000
WARMUP = 3
ITERATIONS = 10
data = [math.sin(i * 0.01) * 100 + 50 for i in range(ROWS)]
s = pd.Series(data)

for _ in range(WARMUP):
s.ewm(span=20).mean()
s.ewm(span=20).std()
s.ewm(span=20).var()

start = time.perf_counter()
for _ in range(ITERATIONS):
s.ewm(span=20).mean()
s.ewm(span=20).std()
s.ewm(span=20).var()
total = (time.perf_counter() - start) * 1000
print(json.dumps({"function": "ewm", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total}))
20 changes: 20 additions & 0 deletions benchmarks/pandas/bench_multi_index_to_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Benchmark: MultiIndex.tolist() on 100k-pair MultiIndex"""
import json, time
import pandas as pd

ROWS = 100_000
WARMUP = 3
ITERATIONS = 10
a = [f"a{i % 100}" for i in range(ROWS)]
b = [i % 1000 for i in range(ROWS)]
tuples = list(zip(a, b))
mi = pd.MultiIndex.from_tuples(tuples)

for _ in range(WARMUP):
mi.tolist()

start = time.perf_counter()
for _ in range(ITERATIONS):
mi.tolist()
total = (time.perf_counter() - start) * 1000
print(json.dumps({"function": "multi_index_to_list", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total}))
79 changes: 79 additions & 0 deletions benchmarks/pandas/bench_register_option.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
Benchmark: register_option — register custom options with pandas' options system.

Mirrors tsb registerOption which wraps pandas' core config register_option API.
Uses pandas.core.config_init / _config._registered_options to register custom
options with defaults and validators.

Outputs JSON: {"function": "register_option", "mean_ms": ..., "iterations": ..., "total_ms": ...}
"""
import json
import time

import pandas as pd

WARMUP = 5
ITERATIONS = 1_000

key_counter = [0]


def register_and_exercise():
key = f"bench.custom_{key_counter[0]}"
key_counter[0] += 1
# pandas does not expose a public register_option in the top-level namespace,
# but it is accessible via pd.core.config.register_option (internal API).
# We simulate the equivalent pattern: register → get → set → reset.
try:
pd.core.config.register_option(key, 42, "A custom numeric option for benchmarking.")
except Exception:
pass # already registered or unavailable
try:
v = pd.get_option(key)
pd.set_option(key, 99)
pd.reset_option(key)
_ = v
except Exception:
pass


def register_with_validator():
key = f"bench.validated_{key_counter[0]}"
key_counter[0] += 1

def validator(val):
if not isinstance(val, (int, float)) or val < 0:
raise ValueError("must be a non-negative number")

try:
pd.core.config.register_option(key, 10, "A validated option.", validator=validator)
except Exception:
pass
try:
pd.set_option(key, 50)
pd.reset_option(key)
except Exception:
pass


# Warm-up
for _ in range(WARMUP):
register_and_exercise()
register_with_validator()

start = time.perf_counter()
for _ in range(ITERATIONS):
register_and_exercise()
register_with_validator()
total_ms = (time.perf_counter() - start) * 1000

print(
json.dumps(
{
"function": "register_option",
"mean_ms": total_ms / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total_ms,
}
)
)
36 changes: 36 additions & 0 deletions benchmarks/pandas/bench_series_digitize_cv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Benchmark: Series.digitize (numpy.digitize) and coefficient of variation on 100k-element Series.

coefficientOfVariation mirrors scipy.stats.variation (std/mean).

Outputs JSON: {"function": "series_digitize_cv", "mean_ms": ..., "iterations": ..., "total_ms": ...}
"""
import json
import time
import numpy as np
import pandas as pd
from scipy.stats import variation

N = 100_000
WARMUP = 3
ITERATIONS = 20

data = [((i * 2654435761) % 1_000_000) / 10_000 for i in range(N)]
s = pd.Series(data)
bins = [i * 5 for i in range(21)]

for _ in range(WARMUP):
np.digitize(s.values, bins)
variation(s.values, ddof=1)

start = time.perf_counter()
for _ in range(ITERATIONS):
np.digitize(s.values, bins)
variation(s.values, ddof=1)
total = (time.perf_counter() - start) * 1000

print(json.dumps({
"function": "series_digitize_cv",
"mean_ms": total / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total,
}))
49 changes: 49 additions & 0 deletions benchmarks/pandas/bench_series_rename_ops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Benchmark: Series.add_prefix / add_suffix / set_axis / DataFrame.set_axis / Series.to_frame
— matching pandas equivalent for bench_series_rename_ops.ts.

Mirrors tsb addPrefixSeries, addSuffixSeries, setAxisSeries, setAxisDataFrame, seriesToFrame.

Outputs JSON: {"function": "series_rename_ops", "mean_ms": ..., "iterations": ..., "total_ms": ...}
"""
import json
import time
import pandas as pd
import numpy as np

SIZE = 100_000
WARMUP = 5
ITERATIONS = 30

data = [i * 0.5 for i in range(SIZE)]
labels = [f"row_{i}" for i in range(SIZE)]
new_labels = [f"new_{i}" for i in range(SIZE)]

s = pd.Series(data, index=labels, name="values")
df = pd.DataFrame({"a": data, "b": [-v for v in data]}, index=labels)

for _ in range(WARMUP):
s.add_prefix("pre_")
s.add_suffix("_suf")
s.set_axis(new_labels)
df.set_axis(new_labels)
s.to_frame()
s.to_frame(name="renamed")

start = time.perf_counter()
for _ in range(ITERATIONS):
s.add_prefix("pre_")
s.add_suffix("_suf")
s.set_axis(new_labels)
df.set_axis(new_labels)
s.to_frame()
s.to_frame(name="renamed")
total = time.perf_counter() - start

total_ms = total * 1000
print(json.dumps({
"function": "series_rename_ops",
"mean_ms": total_ms / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total_ms,
}))
70 changes: 70 additions & 0 deletions benchmarks/pandas/bench_sparse_array_advanced.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
Benchmark: SparseArray.fromSparse / withFillValue / at / SparseDtype

Mirrors tsb's bench_sparse_array_advanced.ts using pandas SparseArray and
SparseDtype equivalents.

Covers:
- pd.arrays.SparseArray(dense, fill_value=np.nan) — construction (fromDense/fromSparse proxy)
- SparseArray.to_dense() — equivalent to at() over all elements
- SparseArray.sp_values / .sp_index — COO-level access
- pd.SparseDtype("float64", fill_value=0.0) — dtype introspection + equality

Dataset: 100k-element sparse array at ~2% density (2k non-zero values).

Outputs JSON: {"function": "sparse_array_advanced", "mean_ms": ..., "iterations": ..., "total_ms": ...}
"""

import json
import math
import time

import numpy as np
import pandas as pd

N = 100_000
DENSITY = 0.02
NNZ = int(N * DENSITY) # 2000 non-zeros
WARMUP = 5
ITERATIONS = 30

# Build dense array with ~2% non-zero values
dense = np.zeros(N, dtype="float64")
for i in range(NNZ):
idx = int((i / NNZ) * N)
dense[idx] = math.sin(i * 0.05) * 100 + 1

def run_once():
# fromSparse equivalent: construct SparseArray from dense (pandas auto-detects sparsity)
sa = pd.arrays.SparseArray(dense, fill_value=float("nan"))

# withFillValue equivalent: pandas does not have this method directly;
# use pd.arrays.SparseArray(sa.to_dense(), fill_value=0.0)
sa2 = pd.arrays.SparseArray(sa.to_dense(), fill_value=0.0)

# at() equivalent: element access via numpy index on sp_values
_ = sa.sp_values[: min(50, len(sa.sp_values))]

# SparseDtype: construction and equality
dt1 = pd.SparseDtype("float64")
dt2 = pd.SparseDtype("float64", fill_value=0.0)
_ = dt1 == dt2

return sa, sa2

# Warm up
for _ in range(WARMUP):
run_once()

# Measure
start = time.perf_counter()
for _ in range(ITERATIONS):
run_once()
total = (time.perf_counter() - start) * 1000 # convert to ms

print(json.dumps({
"function": "sparse_array_advanced",
"mean_ms": total / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total,
}))
Loading
Loading