Skip to content

Commit a92abcc

Browse files
Iteration 131: Add 8 benchmark pairs (372 total, +8 vs best 364)
Add benchmarks for: factorize, get_dummies, nat_sort, to_datetime, to_numeric, select_dtypes, replace_dataframe, pctchange_df. Run: https://github.com/githubnext/tsessebe/actions/runs/24529808007
1 parent e75b53e commit a92abcc

16 files changed

Lines changed: 536 additions & 0 deletions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Benchmark: factorize / pd.Categorical.from_codes — encode values as integer codes."""
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+
categories = ["cat", "dog", "bird", "fish", "hamster"]
11+
data = [categories[i % len(categories)] for i in range(SIZE)]
12+
s = pd.Series(data)
13+
14+
for _ in range(WARMUP):
15+
pd.factorize(data)
16+
s.factorize()
17+
18+
times = []
19+
for _ in range(ITERATIONS):
20+
t0 = time.perf_counter()
21+
pd.factorize(data)
22+
s.factorize()
23+
times.append((time.perf_counter() - t0) * 1000)
24+
25+
total_ms = sum(times)
26+
print(json.dumps({"function": "factorize", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Benchmark: pd.get_dummies — one-hot encoding of categorical data."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 10_000
6+
WARMUP = 3
7+
ITERATIONS = 30
8+
9+
categories = ["A", "B", "C", "D", "E"]
10+
s = pd.Series([categories[i % len(categories)] for i in range(SIZE)])
11+
df = pd.DataFrame({
12+
"cat1": [categories[i % len(categories)] for i in range(SIZE)],
13+
"cat2": [["x", "y", "z"][i % 3] for i in range(SIZE)],
14+
})
15+
16+
for _ in range(WARMUP):
17+
pd.get_dummies(s)
18+
pd.get_dummies(df, columns=["cat1", "cat2"])
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
pd.get_dummies(s)
24+
pd.get_dummies(df, columns=["cat1", "cat2"])
25+
times.append((time.perf_counter() - t0) * 1000)
26+
27+
total_ms = sum(times)
28+
print(json.dumps({"function": "get_dummies", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Benchmark: natural sort using natsort library (equivalent to natSorted/natArgSort)."""
2+
import json, time
3+
4+
SIZE = 10_000
5+
WARMUP = 5
6+
ITERATIONS = 50
7+
8+
data = [f"item{i % 1000}_v{i % 10}" for i in range(SIZE)]
9+
10+
try:
11+
from natsort import natsorted, index_natsorted
12+
def run():
13+
natsorted(data)
14+
index_natsorted(data)
15+
except ImportError:
16+
import re
17+
def nat_key(s):
18+
return [int(c) if c.isdigit() else c.lower() for c in re.split(r'(\d+)', s)]
19+
def run():
20+
sorted(data, key=nat_key)
21+
sorted(range(len(data)), key=lambda i: nat_key(data[i]))
22+
23+
for _ in range(WARMUP):
24+
run()
25+
26+
times = []
27+
for _ in range(ITERATIONS):
28+
t0 = time.perf_counter()
29+
run()
30+
times.append((time.perf_counter() - t0) * 1000)
31+
32+
total_ms = sum(times)
33+
print(json.dumps({"function": "nat_sort", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Benchmark: DataFrame.pct_change — percentage change across DataFrame columns."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
df = pd.DataFrame({
10+
"a": [i * 1.1 + 1 for i in range(SIZE)],
11+
"b": [i * 0.5 + 2 for i in range(SIZE)],
12+
"c": [i * 2.3 + 3 for i in range(SIZE)],
13+
})
14+
15+
for _ in range(WARMUP):
16+
df.pct_change()
17+
df.pct_change(periods=3)
18+
19+
times = []
20+
for _ in range(ITERATIONS):
21+
t0 = time.perf_counter()
22+
df.pct_change()
23+
df.pct_change(periods=3)
24+
times.append((time.perf_counter() - t0) * 1000)
25+
26+
total_ms = sum(times)
27+
print(json.dumps({"function": "pctchange_df", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Benchmark: DataFrame.replace — replace values in a DataFrame."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
df = pd.DataFrame({
10+
"a": [i % 10 for i in range(SIZE)],
11+
"b": [i % 5 for i in range(SIZE)],
12+
"c": [["x", "y", "z"][i % 3] for i in range(SIZE)],
13+
})
14+
mapping = {0: 100, 1: 200, 2: 300}
15+
16+
for _ in range(WARMUP):
17+
df.replace(mapping)
18+
19+
times = []
20+
for _ in range(ITERATIONS):
21+
t0 = time.perf_counter()
22+
df.replace(mapping)
23+
times.append((time.perf_counter() - t0) * 1000)
24+
25+
total_ms = sum(times)
26+
print(json.dumps({"function": "replace_dataframe", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Benchmark: DataFrame.select_dtypes — filter columns by dtype."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
df = pd.DataFrame({
10+
"a": list(range(SIZE)),
11+
"b": [i * 1.5 for i in range(SIZE)],
12+
"c": [f"str{i % 1000}" for i in range(SIZE)],
13+
"d": [i % 2 == 0 for i in range(SIZE)],
14+
"e": list(range(0, SIZE * 2, 2)),
15+
"f": [f"label{i % 100}" for i in range(SIZE)],
16+
})
17+
18+
for _ in range(WARMUP):
19+
df.select_dtypes(include=["number"])
20+
df.select_dtypes(include=["object"])
21+
df.select_dtypes(exclude=["bool"])
22+
23+
times = []
24+
for _ in range(ITERATIONS):
25+
t0 = time.perf_counter()
26+
df.select_dtypes(include=["number"])
27+
df.select_dtypes(include=["object"])
28+
df.select_dtypes(exclude=["bool"])
29+
times.append((time.perf_counter() - t0) * 1000)
30+
31+
total_ms = sum(times)
32+
print(json.dumps({"function": "select_dtypes", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Benchmark: pd.to_datetime — parse string/numeric values to datetime."""
2+
import json, time
3+
import pandas as pd
4+
from datetime import datetime, timedelta
5+
6+
SIZE = 10_000
7+
WARMUP = 5
8+
ITERATIONS = 50
9+
10+
base = datetime(2020, 1, 1)
11+
date_strings = [(base + timedelta(days=i)).strftime("%Y-%m-%d") for i in range(SIZE)]
12+
timestamps = [int((base + timedelta(days=i)).timestamp() * 1000) for i in range(SIZE)]
13+
14+
for _ in range(WARMUP):
15+
pd.to_datetime(date_strings)
16+
pd.to_datetime(timestamps, unit="ms")
17+
18+
times = []
19+
for _ in range(ITERATIONS):
20+
t0 = time.perf_counter()
21+
pd.to_datetime(date_strings)
22+
pd.to_datetime(timestamps, unit="ms")
23+
times.append((time.perf_counter() - t0) * 1000)
24+
25+
total_ms = sum(times)
26+
print(json.dumps({"function": "to_datetime", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Benchmark: pd.to_numeric — coerce string arrays to numeric."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
str_nums = [str(i * 1.5) for i in range(SIZE)]
10+
s = pd.Series(str_nums)
11+
12+
for _ in range(WARMUP):
13+
pd.to_numeric(str_nums, errors="coerce")
14+
pd.to_numeric(s, errors="coerce")
15+
16+
times = []
17+
for _ in range(ITERATIONS):
18+
t0 = time.perf_counter()
19+
pd.to_numeric(str_nums, errors="coerce")
20+
pd.to_numeric(s, errors="coerce")
21+
times.append((time.perf_counter() - t0) * 1000)
22+
23+
total_ms = sum(times)
24+
print(json.dumps({"function": "to_numeric", "mean_ms": round(total_ms / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total_ms, 3)}))

benchmarks/tsb/bench_factorize.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Benchmark: factorize / seriesFactorize — encode values as integer codes.
3+
* Outputs JSON: {"function": "factorize", "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 = 50;
10+
11+
const categories = ["cat", "dog", "bird", "fish", "hamster"];
12+
const data = Array.from({ length: SIZE }, (_, i) => categories[i % categories.length]);
13+
const s = new Series({ data });
14+
15+
for (let i = 0; i < WARMUP; i++) {
16+
factorize(data);
17+
seriesFactorize(s);
18+
}
19+
20+
const times: number[] = [];
21+
for (let i = 0; i < ITERATIONS; i++) {
22+
const start = performance.now();
23+
factorize(data);
24+
seriesFactorize(s);
25+
times.push(performance.now() - start);
26+
}
27+
28+
const totalMs = times.reduce((a, b) => a + b, 0);
29+
const meanMs = totalMs / ITERATIONS;
30+
console.log(
31+
JSON.stringify({
32+
function: "factorize",
33+
mean_ms: Math.round(meanMs * 1000) / 1000,
34+
iterations: ITERATIONS,
35+
total_ms: Math.round(totalMs * 1000) / 1000,
36+
}),
37+
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Benchmark: getDummies / dataFrameGetDummies — one-hot encoding.
3+
* Outputs JSON: {"function": "get_dummies", "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 = 3;
9+
const ITERATIONS = 30;
10+
11+
const categories = ["A", "B", "C", "D", "E"];
12+
const s = new Series({ data: Array.from({ length: SIZE }, (_, i) => categories[i % categories.length]) });
13+
const df = new DataFrame({
14+
cat1: Array.from({ length: SIZE }, (_, i) => categories[i % categories.length]),
15+
cat2: Array.from({ length: SIZE }, (_, i) => ["x", "y", "z"][i % 3]),
16+
});
17+
18+
for (let i = 0; i < WARMUP; i++) {
19+
getDummies(s);
20+
dataFrameGetDummies(df, { columns: ["cat1", "cat2"] });
21+
}
22+
23+
const times: number[] = [];
24+
for (let i = 0; i < ITERATIONS; i++) {
25+
const start = performance.now();
26+
getDummies(s);
27+
dataFrameGetDummies(df, { columns: ["cat1", "cat2"] });
28+
times.push(performance.now() - start);
29+
}
30+
31+
const totalMs = times.reduce((a, b) => a + b, 0);
32+
const meanMs = totalMs / ITERATIONS;
33+
console.log(
34+
JSON.stringify({
35+
function: "get_dummies",
36+
mean_ms: Math.round(meanMs * 1000) / 1000,
37+
iterations: ITERATIONS,
38+
total_ms: Math.round(totalMs * 1000) / 1000,
39+
}),
40+
);

0 commit comments

Comments
 (0)