anchorboosting 0.2.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.github/workflows/ci.yml +22 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/CHANGELOG.rst +6 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/PKG-INFO +11 -4
- anchorboosting-0.3.0/README.md +14 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting/models.py +3 -1
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting/simulate.py +10 -1
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/PKG-INFO +11 -4
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/SOURCES.txt +6 -0
- anchorboosting-0.3.0/asv.conf.json +18 -0
- anchorboosting-0.3.0/benchmarks/__init__.py +0 -0
- anchorboosting-0.3.0/benchmarks/benchmark.py +77 -0
- anchorboosting-0.3.0/benchmarks/data/.gitignore +1 -0
- anchorboosting-0.3.0/benchmarks/environment.yml +14 -0
- anchorboosting-0.3.0/benchmarks/load.py +110 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/index.rst +1 -1
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/environment.yml +1 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/pyproject.toml +1 -1
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/tests/test_anchor_boosting.py +52 -8
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/tests/test_refit.py +3 -2
- anchorboosting-0.2.1/README.md +0 -7
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.github/workflows/build.yaml +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.gitignore +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.pre-commit-config.yaml +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/LICENSE +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting/__init__.py +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/dependency_links.txt +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/requires.txt +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/top_level.txt +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/Makefile +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/bib.bib +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/changelog.rst +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/conf.py +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/readthedocs.yaml +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/setup.cfg +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/tests/test_derivatives.py +0 -0
- {anchorboosting-0.2.1 → anchorboosting-0.3.0}/tests/test_proj.py +0 -0
|
@@ -8,6 +8,28 @@ name: CI
|
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
10
|
|
|
11
|
+
asv-bench:
|
|
12
|
+
name: ASV Benchmarks
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
- name: Set up micromamba
|
|
19
|
+
uses: mamba-org/setup-micromamba@d05808540d968a55ca33c798e0661fb98f533c73
|
|
20
|
+
with:
|
|
21
|
+
micromamba-version: 1.5.10-0
|
|
22
|
+
environment-file: benchmarks/environment.yml
|
|
23
|
+
|
|
24
|
+
- name: Install repository
|
|
25
|
+
run: python -m pip install --no-build-isolation --no-deps --disable-pip-version-check -e .
|
|
26
|
+
- name: Run benchmarks
|
|
27
|
+
shell: bash -l {0}
|
|
28
|
+
run: |
|
|
29
|
+
asv machine --machine github --yes
|
|
30
|
+
# asv run --machine github --python=same
|
|
31
|
+
asv continuous origin/main HEAD -e --factor 1.05 --machine github --python=same
|
|
32
|
+
|
|
11
33
|
test:
|
|
12
34
|
name: Unit tests - ${{ matrix.PYTHON_VERSION }}
|
|
13
35
|
runs-on: ubuntu-latest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anchorboosting
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Efficient tree-based nonlinear anchor regression and classification
|
|
5
5
|
Author-email: Malte Londschien <malte@londschien.ch>
|
|
6
6
|
Project-URL: homepage, https://github.com/mlondschien/anchorboosting/
|
|
@@ -15,8 +15,15 @@ Dynamic: license-file
|
|
|
15
15
|
|
|
16
16
|
# Anchor Boosting
|
|
17
17
|
|
|
18
|
-
Efficient tree-based nonlinear anchor regression and classification for Python
|
|
18
|
+
Efficient tree-based nonlinear anchor regression and classification for Python.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
If you use this, please consider citing
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
```
|
|
23
|
+
article{londschien2025,
|
|
24
|
+
title={Domain Generalization and Adaptation in Intensive Care with Anchor Regression},
|
|
25
|
+
author={Londschien, Malte, Burger, Manuel, R{\"a}tsch, Gunnar and B{\"u}hlmann, Peter},
|
|
26
|
+
journal={arXiv preprint arXiv:2507.21783},
|
|
27
|
+
year={2025}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Anchor Boosting
|
|
2
|
+
|
|
3
|
+
Efficient tree-based nonlinear anchor regression and classification for Python.
|
|
4
|
+
|
|
5
|
+
If you use this, please consider citing
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
article{londschien2025,
|
|
9
|
+
title={Domain Generalization and Adaptation in Intensive Care with Anchor Regression},
|
|
10
|
+
author={Londschien, Malte, Burger, Manuel, R{\"a}tsch, Gunnar and B{\"u}hlmann, Peter},
|
|
11
|
+
journal={arXiv preprint arXiv:2507.21783},
|
|
12
|
+
year={2025}
|
|
13
|
+
}
|
|
14
|
+
```
|
|
@@ -2,7 +2,7 @@ import numpy as np
|
|
|
2
2
|
import polars as pl
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def simulate(f, n=100, shift=0, seed=0):
|
|
5
|
+
def simulate(f, n=100, shift=0, seed=0, return_dtype="polars"):
|
|
6
6
|
rng = np.random.RandomState(seed)
|
|
7
7
|
|
|
8
8
|
p = 3
|
|
@@ -23,6 +23,15 @@ def simulate(f, n=100, shift=0, seed=0):
|
|
|
23
23
|
schema={"x1": pl.Float64, "x2": pl.Float64, "x3": pl.Int64},
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
+
if return_dtype == "numpy":
|
|
27
|
+
df = df.to_numpy()
|
|
28
|
+
a = a.astype(np.float32)
|
|
29
|
+
y = y.astype(np.float32)
|
|
30
|
+
elif return_dtype == "pandas":
|
|
31
|
+
df = df.to_pandas()
|
|
32
|
+
a = a.astype(np.float32)
|
|
33
|
+
y = y.astype(np.float32)
|
|
34
|
+
|
|
26
35
|
return df, y, a
|
|
27
36
|
|
|
28
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anchorboosting
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Efficient tree-based nonlinear anchor regression and classification
|
|
5
5
|
Author-email: Malte Londschien <malte@londschien.ch>
|
|
6
6
|
Project-URL: homepage, https://github.com/mlondschien/anchorboosting/
|
|
@@ -15,8 +15,15 @@ Dynamic: license-file
|
|
|
15
15
|
|
|
16
16
|
# Anchor Boosting
|
|
17
17
|
|
|
18
|
-
Efficient tree-based nonlinear anchor regression and classification for Python
|
|
18
|
+
Efficient tree-based nonlinear anchor regression and classification for Python.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
If you use this, please consider citing
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
```
|
|
23
|
+
article{londschien2025,
|
|
24
|
+
title={Domain Generalization and Adaptation in Intensive Care with Anchor Regression},
|
|
25
|
+
author={Londschien, Malte, Burger, Manuel, R{\"a}tsch, Gunnar and B{\"u}hlmann, Peter},
|
|
26
|
+
journal={arXiv preprint arXiv:2507.21783},
|
|
27
|
+
year={2025}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
CHANGELOG.rst
|
|
4
4
|
LICENSE
|
|
5
5
|
README.md
|
|
6
|
+
asv.conf.json
|
|
6
7
|
environment.yml
|
|
7
8
|
pyproject.toml
|
|
8
9
|
readthedocs.yaml
|
|
@@ -16,6 +17,11 @@ anchorboosting.egg-info/SOURCES.txt
|
|
|
16
17
|
anchorboosting.egg-info/dependency_links.txt
|
|
17
18
|
anchorboosting.egg-info/requires.txt
|
|
18
19
|
anchorboosting.egg-info/top_level.txt
|
|
20
|
+
benchmarks/__init__.py
|
|
21
|
+
benchmarks/benchmark.py
|
|
22
|
+
benchmarks/environment.yml
|
|
23
|
+
benchmarks/load.py
|
|
24
|
+
benchmarks/data/.gitignore
|
|
19
25
|
docs/Makefile
|
|
20
26
|
docs/bib.bib
|
|
21
27
|
docs/changelog.rst
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"branches": ["main"],
|
|
4
|
+
"project": "anchorboosting",
|
|
5
|
+
"project_url": "https://github.com/mlondschien/anchorboosting",
|
|
6
|
+
"repo": "..",
|
|
7
|
+
"environment_type": "conda",
|
|
8
|
+
"show_commit_url": "https://github.com/mlondschien/anchorboosting/commit/",
|
|
9
|
+
"pythons": ["3.12"],
|
|
10
|
+
"conda_channels": ["conda-forge"],
|
|
11
|
+
"build_command": "",
|
|
12
|
+
"install_command": "python -mpip install --no-deps .",
|
|
13
|
+
"conda_environment_file": "benchmarks/environment.yml",
|
|
14
|
+
"benchmark_dir": "benchmarks",
|
|
15
|
+
"env_dir": ".asv/env",
|
|
16
|
+
"results_dir": ".asv/results",
|
|
17
|
+
"html_dir": ".asv/html"
|
|
18
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import timeit
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from lightgbm import LGBMModel
|
|
6
|
+
|
|
7
|
+
from anchorboosting import AnchorBooster
|
|
8
|
+
|
|
9
|
+
from .load import load_nyc_taxi
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Benchmark(ABC):
|
|
13
|
+
# Measure wall time instead of CPU usage
|
|
14
|
+
timer = timeit.default_timer
|
|
15
|
+
|
|
16
|
+
name = ""
|
|
17
|
+
|
|
18
|
+
params: Any
|
|
19
|
+
param_names: Any
|
|
20
|
+
|
|
21
|
+
def setup(self, *params):
|
|
22
|
+
self._setup_data(params)
|
|
23
|
+
self._setup_model(params)
|
|
24
|
+
|
|
25
|
+
def _setup_data(self, params):
|
|
26
|
+
idx = self.param_names.index("n")
|
|
27
|
+
n = params[idx]
|
|
28
|
+
self.X, self.Z, self.y, self.y_binary = load_nyc_taxi(n)
|
|
29
|
+
|
|
30
|
+
@abstractmethod # Required for asv to skip.
|
|
31
|
+
def _setup_model(self, params):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AnchorBoosterBenchmark(Benchmark):
|
|
36
|
+
def __init__(self, n_jobs=2):
|
|
37
|
+
self.n_jobs = n_jobs # GitHub CI has two nodes.
|
|
38
|
+
|
|
39
|
+
name = "AnchorBooster"
|
|
40
|
+
param_names = ["objective", "n", "num_boost_round", "max_depth"]
|
|
41
|
+
params = (["regression", "binary"], [100_000], [100], [3])
|
|
42
|
+
|
|
43
|
+
def _setup_model(self, params):
|
|
44
|
+
objective, _, num_boost_round, max_depth = params
|
|
45
|
+
self.model = AnchorBooster(
|
|
46
|
+
gamma=2,
|
|
47
|
+
num_boost_round=num_boost_round,
|
|
48
|
+
max_depth=max_depth,
|
|
49
|
+
n_jobs=self.n_jobs,
|
|
50
|
+
objective=objective,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def time_fit(self, *args):
|
|
54
|
+
y = self.y_binary if self.model.objective == "binary" else self.y
|
|
55
|
+
self.model.fit(self.X, y, self.Z)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LGBMBenchmark(Benchmark):
|
|
59
|
+
def __init__(self, n_jobs=2):
|
|
60
|
+
self.n_jobs = n_jobs # GitHub CI has two nodes.
|
|
61
|
+
|
|
62
|
+
name = "LGBM Booster"
|
|
63
|
+
param_names = ["objective", "n", "num_boost_round", "max_depth"]
|
|
64
|
+
params = (["regression", "binary"], [100_000], [100], [3])
|
|
65
|
+
|
|
66
|
+
def _setup_model(self, params):
|
|
67
|
+
objective, _, num_boost_round, max_depth = params
|
|
68
|
+
self.model = LGBMModel(
|
|
69
|
+
num_boost_round=num_boost_round,
|
|
70
|
+
max_depth=max_depth,
|
|
71
|
+
objective=objective,
|
|
72
|
+
n_jobs=self.n_jobs,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def time_fit(self, *args):
|
|
76
|
+
y = self.y_binary if self.model.objective == "binary" else self.y
|
|
77
|
+
self.model.fit(self.X, y)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.parquet
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: benchmarks
|
|
2
|
+
channels:
|
|
3
|
+
- conda-forge
|
|
4
|
+
- nodefaults
|
|
5
|
+
dependencies:
|
|
6
|
+
- python =3.12
|
|
7
|
+
- numpy
|
|
8
|
+
- lightgbm
|
|
9
|
+
- asv
|
|
10
|
+
- scikit-learn <=1.5.2 # https://github.com/microsoft/LightGBM/issues/6849
|
|
11
|
+
- polars
|
|
12
|
+
- pyarrow
|
|
13
|
+
- cffi # https://github.com/microsoft/LightGBM/pull/6785
|
|
14
|
+
- libmambapy
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# From https://github.com/xhochy/nyc-taxi-fare-prediction-deployment-example
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import polars as pl
|
|
5
|
+
from sklearn.compose import ColumnTransformer
|
|
6
|
+
from sklearn.preprocessing import OrdinalEncoder
|
|
7
|
+
|
|
8
|
+
DATA_PATH = Path(__file__).resolve().parent / "data"
|
|
9
|
+
NYC_TAXI_DATASET_PATH = DATA_PATH / "nyc_taxi_data.parquet"
|
|
10
|
+
NYC_TAXI_DATASET_URL = (
|
|
11
|
+
"https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2019-01.parquet"
|
|
12
|
+
)
|
|
13
|
+
NYC_TAXI_BURROWS_PATH = DATA_PATH / "nyc_taxi_burrows.parquet"
|
|
14
|
+
NYC_TAXI_BURROWS_URL = "https://d37ci6vzurychx.cloudfront.net/misc/taxi_zone_lookup.csv"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_nyc_taxi_raw():
|
|
18
|
+
if NYC_TAXI_DATASET_PATH.exists():
|
|
19
|
+
return pl.read_parquet(NYC_TAXI_DATASET_PATH)
|
|
20
|
+
else:
|
|
21
|
+
df = pl.read_parquet(NYC_TAXI_DATASET_URL)
|
|
22
|
+
df.write_parquet(NYC_TAXI_DATASET_PATH)
|
|
23
|
+
return df
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def load_nyc_taxi_burrows():
|
|
27
|
+
if NYC_TAXI_BURROWS_PATH.exists():
|
|
28
|
+
return pl.read_parquet(NYC_TAXI_BURROWS_PATH)
|
|
29
|
+
else:
|
|
30
|
+
df = pl.read_csv(NYC_TAXI_BURROWS_URL)
|
|
31
|
+
df.write_parquet(NYC_TAXI_BURROWS_PATH)
|
|
32
|
+
return df
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load_nyc_taxi(n):
|
|
36
|
+
df = load_nyc_taxi_raw().head(n)
|
|
37
|
+
|
|
38
|
+
burrows = load_nyc_taxi_burrows()
|
|
39
|
+
|
|
40
|
+
df = (
|
|
41
|
+
df.filter(pl.col("total_amount").is_not_null() & pl.col("total_amount").gt(0))
|
|
42
|
+
.join(
|
|
43
|
+
burrows.with_columns(pl.col("Borough").alias("pickup_borough")),
|
|
44
|
+
left_on="PULocationID",
|
|
45
|
+
right_on="LocationID",
|
|
46
|
+
how="left",
|
|
47
|
+
)
|
|
48
|
+
.join(
|
|
49
|
+
burrows.with_columns(pl.col("Borough").alias("dropoff_borough")),
|
|
50
|
+
left_on="DOLocationID",
|
|
51
|
+
right_on="LocationID",
|
|
52
|
+
how="left",
|
|
53
|
+
)
|
|
54
|
+
.with_columns(
|
|
55
|
+
pl.col("total_amount").log().alias("total_amount_log"),
|
|
56
|
+
pl.col("tpep_pickup_datetime").dt.weekday().alias("pickup_weekday"),
|
|
57
|
+
pl.col("tpep_pickup_datetime").dt.hour().alias("pickup_hour"),
|
|
58
|
+
pl.col("tpep_pickup_datetime").dt.minute().alias("pickup_minute"),
|
|
59
|
+
pl.col("pickup_borough").fill_null("Unknown").alias("pickup_borough"),
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
continuous_columns = [
|
|
64
|
+
"passenger_count",
|
|
65
|
+
"trip_distance",
|
|
66
|
+
"extra",
|
|
67
|
+
"mta_tax",
|
|
68
|
+
"tip_amount",
|
|
69
|
+
"tolls_amount",
|
|
70
|
+
"improvement_surcharge",
|
|
71
|
+
"congestion_surcharge",
|
|
72
|
+
"pickup_hour",
|
|
73
|
+
"pickup_minute",
|
|
74
|
+
]
|
|
75
|
+
df = df.with_columns(pl.col(c).cast(pl.Float64) for c in continuous_columns)
|
|
76
|
+
|
|
77
|
+
ordinal_columns = [
|
|
78
|
+
"VendorID",
|
|
79
|
+
"RatecodeID",
|
|
80
|
+
"store_and_fwd_flag",
|
|
81
|
+
"payment_type",
|
|
82
|
+
"service_zone",
|
|
83
|
+
"Borough",
|
|
84
|
+
"Zone",
|
|
85
|
+
"pickup_weekday",
|
|
86
|
+
"pickup_borough",
|
|
87
|
+
"dropoff_borough",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
preprocessor = ColumnTransformer(
|
|
91
|
+
transformers=[
|
|
92
|
+
("continuous", "passthrough", continuous_columns),
|
|
93
|
+
(
|
|
94
|
+
"categorical",
|
|
95
|
+
OrdinalEncoder(
|
|
96
|
+
handle_unknown="use_encoded_value",
|
|
97
|
+
unknown_value=-1,
|
|
98
|
+
),
|
|
99
|
+
ordinal_columns,
|
|
100
|
+
),
|
|
101
|
+
]
|
|
102
|
+
).set_output(transform="polars")
|
|
103
|
+
|
|
104
|
+
y = df.select(pl.col("total_amount_log")).to_numpy().ravel()
|
|
105
|
+
y_binary = (y > y.mean()).astype(int)
|
|
106
|
+
|
|
107
|
+
X = preprocessor.fit_transform(df)
|
|
108
|
+
Z = X.select("categorical__Zone").to_numpy().astype("int32").ravel()
|
|
109
|
+
|
|
110
|
+
return X.drop("categorical__Zone"), Z, y, y_binary
|
|
@@ -5,7 +5,7 @@ requires = ['setuptools', 'setuptools-scm', 'wheel']
|
|
|
5
5
|
name = "anchorboosting"
|
|
6
6
|
description = "Efficient tree-based nonlinear anchor regression and classification"
|
|
7
7
|
readme = "README.md"
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.3.0"
|
|
9
9
|
requires-python = ">=3.9"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name = "Malte Londschien", email = "malte@londschien.ch" }
|
|
@@ -10,13 +10,14 @@ from anchorboosting.simulate import f1, simulate
|
|
|
10
10
|
@pytest.mark.parametrize("gamma", [1.0, 2.0, 100])
|
|
11
11
|
@pytest.mark.parametrize("objective", ["regression", "binary"])
|
|
12
12
|
@pytest.mark.parametrize("categorical_z", [True, False])
|
|
13
|
-
|
|
13
|
+
@pytest.mark.parametrize("input_dtype", ["polars", "numpy"])
|
|
14
|
+
def test_anchor_boosting_second_order(gamma, objective, categorical_z, input_dtype):
|
|
14
15
|
learning_rate = 0.1
|
|
15
16
|
num_leaves = 5
|
|
16
17
|
n = 200
|
|
17
18
|
num_boost_round = 10
|
|
18
19
|
|
|
19
|
-
x, y, a = simulate(f1, n=n, shift=0, seed=0)
|
|
20
|
+
x, y, a = simulate(f1, n=n, shift=0, seed=0, return_dtype=input_dtype)
|
|
20
21
|
if categorical_z:
|
|
21
22
|
a = np.digitize(a[:, 0], bins=[-1, 0, 1, 2, 3])
|
|
22
23
|
assert np.issubdtype(a.dtype, np.integer)
|
|
@@ -36,7 +37,7 @@ def test_anchor_boosting_second_order(gamma, objective, categorical_z):
|
|
|
36
37
|
f = model.predict(x, num_iteration=num_boost_round - 1, raw_score=True)
|
|
37
38
|
|
|
38
39
|
leaves = model.booster_.predict(
|
|
39
|
-
x.to_arrow(),
|
|
40
|
+
x.to_arrow() if hasattr(x, "to_arrow") else x,
|
|
40
41
|
pred_leaf=True,
|
|
41
42
|
start_iteration=num_boost_round - 1,
|
|
42
43
|
num_iteration=1,
|
|
@@ -144,8 +145,19 @@ def test_anchor_boosting_decreases_loss(gamma, objective):
|
|
|
144
145
|
{"lambda_l2": 0.1},
|
|
145
146
|
],
|
|
146
147
|
)
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
@pytest.mark.parametrize("input_dtype", ["polars", "numpy", "pandas"])
|
|
149
|
+
def test_compare_anchor_boosting_to_lgbm(parameters, input_dtype):
|
|
150
|
+
X, y, a = simulate(f1, shift=0, seed=0, return_dtype=input_dtype)
|
|
151
|
+
|
|
152
|
+
if input_dtype == "polars":
|
|
153
|
+
X_arrow = X.to_arrow()
|
|
154
|
+
categorical_feature = ["x3"]
|
|
155
|
+
elif input_dtype == "pandas":
|
|
156
|
+
X_arrow = X
|
|
157
|
+
categorical_feature = ["x3"]
|
|
158
|
+
elif input_dtype == "numpy":
|
|
159
|
+
X_arrow = X
|
|
160
|
+
categorical_feature = [2]
|
|
149
161
|
|
|
150
162
|
lgbm_model = lgb.train(
|
|
151
163
|
params={
|
|
@@ -153,7 +165,7 @@ def test_compare_anchor_boosting_to_lgbm(parameters):
|
|
|
153
165
|
"objective": "regression",
|
|
154
166
|
**parameters,
|
|
155
167
|
},
|
|
156
|
-
train_set=lgb.Dataset(
|
|
168
|
+
train_set=lgb.Dataset(X_arrow, y, categorical_feature=[2]),
|
|
157
169
|
num_boost_round=50,
|
|
158
170
|
)
|
|
159
171
|
|
|
@@ -163,7 +175,7 @@ def test_compare_anchor_boosting_to_lgbm(parameters):
|
|
|
163
175
|
objective="regression",
|
|
164
176
|
learning_rate=0.1,
|
|
165
177
|
**parameters,
|
|
166
|
-
).fit(X, y, Z=a, categorical_feature=
|
|
178
|
+
).fit(X, y, Z=a, categorical_feature=categorical_feature)
|
|
167
179
|
|
|
168
180
|
anchor_booster_noncat = AnchorBooster(
|
|
169
181
|
gamma=1,
|
|
@@ -173,7 +185,7 @@ def test_compare_anchor_boosting_to_lgbm(parameters):
|
|
|
173
185
|
**parameters,
|
|
174
186
|
).fit(X, y, Z=a)
|
|
175
187
|
|
|
176
|
-
lgbm_pred = lgbm_model.predict(
|
|
188
|
+
lgbm_pred = lgbm_model.predict(X_arrow)
|
|
177
189
|
anchor_booster_pred = anchor_booster.predict(X)
|
|
178
190
|
anchor_booster_noncat_pred = anchor_booster_noncat.predict(X)
|
|
179
191
|
|
|
@@ -181,6 +193,38 @@ def test_compare_anchor_boosting_to_lgbm(parameters):
|
|
|
181
193
|
assert not np.allclose(lgbm_pred, anchor_booster_noncat_pred)
|
|
182
194
|
|
|
183
195
|
|
|
196
|
+
@pytest.mark.parametrize("gamma", [1, 10])
|
|
197
|
+
def test_compare_input_types(gamma):
|
|
198
|
+
X_polars, y_polars, a_polars = simulate(f1, shift=0, seed=0, return_dtype="polars")
|
|
199
|
+
X_numpy, y_numpy, a_numpy = simulate(f1, shift=0, seed=0, return_dtype="numpy")
|
|
200
|
+
X_pandas, y_pandas, a_pandas = simulate(f1, shift=0, seed=0, return_dtype="pandas")
|
|
201
|
+
|
|
202
|
+
model_polars = AnchorBooster(
|
|
203
|
+
gamma=gamma,
|
|
204
|
+
num_boost_round=10,
|
|
205
|
+
objective="regression",
|
|
206
|
+
).fit(X_polars, y_polars, Z=a_polars)
|
|
207
|
+
|
|
208
|
+
model_numpy = AnchorBooster(
|
|
209
|
+
gamma=gamma,
|
|
210
|
+
num_boost_round=10,
|
|
211
|
+
objective="regression",
|
|
212
|
+
).fit(X_numpy, y_numpy, Z=a_numpy)
|
|
213
|
+
|
|
214
|
+
model_pandas = AnchorBooster(
|
|
215
|
+
gamma=gamma,
|
|
216
|
+
num_boost_round=10,
|
|
217
|
+
objective="regression",
|
|
218
|
+
).fit(X_pandas, y_pandas, Z=a_pandas)
|
|
219
|
+
|
|
220
|
+
pred_pandas = model_pandas.predict(X_pandas)
|
|
221
|
+
pred_polars = model_polars.predict(X_polars)
|
|
222
|
+
pred_numpy = model_numpy.predict(X_numpy)
|
|
223
|
+
|
|
224
|
+
np.testing.assert_allclose(pred_polars, pred_numpy, rtol=1e-5)
|
|
225
|
+
np.testing.assert_allclose(pred_polars, pred_pandas, rtol=1e-5)
|
|
226
|
+
|
|
227
|
+
|
|
184
228
|
@pytest.mark.parametrize("objective", ["regression", "binary"])
|
|
185
229
|
def test_categorical_and_onehot_is_the_same(objective):
|
|
186
230
|
X, y, a = simulate(f1, shift=0, seed=0)
|
|
@@ -9,9 +9,10 @@ from anchorboosting.simulate import f1, f2, simulate
|
|
|
9
9
|
@pytest.mark.parametrize("seed", [0, 1])
|
|
10
10
|
@pytest.mark.parametrize("fun", [f1, f2])
|
|
11
11
|
@pytest.mark.parametrize("objective", ["regression", "binary"])
|
|
12
|
-
|
|
12
|
+
@pytest.mark.parametrize("input_dtype", ["numpy", "pandas", "polars"])
|
|
13
|
+
def test_refit(objective, seed, fun, input_dtype):
|
|
13
14
|
|
|
14
|
-
X, y, a = simulate(fun, shift=0, seed=seed)
|
|
15
|
+
X, y, a = simulate(fun, shift=0, seed=seed, return_dtype=input_dtype)
|
|
15
16
|
|
|
16
17
|
if objective == "binary":
|
|
17
18
|
y = (y > 0).astype(int)
|
anchorboosting-0.2.1/README.md
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|