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.
Files changed (36) hide show
  1. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.github/workflows/ci.yml +22 -0
  2. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/CHANGELOG.rst +6 -0
  3. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/PKG-INFO +11 -4
  4. anchorboosting-0.3.0/README.md +14 -0
  5. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting/models.py +3 -1
  6. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting/simulate.py +10 -1
  7. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/PKG-INFO +11 -4
  8. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/SOURCES.txt +6 -0
  9. anchorboosting-0.3.0/asv.conf.json +18 -0
  10. anchorboosting-0.3.0/benchmarks/__init__.py +0 -0
  11. anchorboosting-0.3.0/benchmarks/benchmark.py +77 -0
  12. anchorboosting-0.3.0/benchmarks/data/.gitignore +1 -0
  13. anchorboosting-0.3.0/benchmarks/environment.yml +14 -0
  14. anchorboosting-0.3.0/benchmarks/load.py +110 -0
  15. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/index.rst +1 -1
  16. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/environment.yml +1 -0
  17. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/pyproject.toml +1 -1
  18. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/tests/test_anchor_boosting.py +52 -8
  19. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/tests/test_refit.py +3 -2
  20. anchorboosting-0.2.1/README.md +0 -7
  21. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.github/workflows/build.yaml +0 -0
  22. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.gitignore +0 -0
  23. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/.pre-commit-config.yaml +0 -0
  24. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/LICENSE +0 -0
  25. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting/__init__.py +0 -0
  26. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/dependency_links.txt +0 -0
  27. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/requires.txt +0 -0
  28. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/anchorboosting.egg-info/top_level.txt +0 -0
  29. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/Makefile +0 -0
  30. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/bib.bib +0 -0
  31. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/changelog.rst +0 -0
  32. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/docs/conf.py +0 -0
  33. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/readthedocs.yaml +0 -0
  34. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/setup.cfg +0 -0
  35. {anchorboosting-0.2.1 → anchorboosting-0.3.0}/tests/test_derivatives.py +0 -0
  36. {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,12 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 0.3.0 (2025-09-15)
5
+ ------------------
6
+
7
+ - Fixed handling of feature names for numpy input in `AnchorBooster`.
8
+ - Added support for pandas DataFrame input in `AnchorBooster`.
9
+
4
10
  0.2.1 (2025-07-01)
5
11
  ------------------
6
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anchorboosting
3
- Version: 0.2.1
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 [1]
18
+ Efficient tree-based nonlinear anchor regression and classification for Python.
19
19
 
20
- ## References
20
+ If you use this, please consider citing
21
21
 
22
- [1] Londschien, M., M. Burger, G. Rätsch, P. Bühlmann (2025). Working paper.
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
+ ```
@@ -174,7 +174,9 @@ class AnchorBooster:
174
174
  )
175
175
 
176
176
  if hasattr(X, "columns"):
177
- feature_name = X.columns
177
+ feature_name = list(X.columns)
178
+ else:
179
+ feature_name = None
178
180
 
179
181
  if _POLARS_INSTALLED and isinstance(X, pl.DataFrame):
180
182
  X = X.to_arrow()
@@ -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.2.1
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 [1]
18
+ Efficient tree-based nonlinear anchor regression and classification for Python.
19
19
 
20
- ## References
20
+ If you use this, please consider citing
21
21
 
22
- [1] Londschien, M., M. Burger, G. Rätsch, P. Bühlmann (2025). Working paper.
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
@@ -30,7 +30,7 @@ API Reference
30
30
  :maxdepth: 1
31
31
  :caption: Other
32
32
 
33
- GitHub <https://github.com/mlondschien/ivmodels>
33
+ GitHub <https://github.com/mlondschien/anchorboosting>
34
34
  changelog
35
35
 
36
36
 
@@ -11,6 +11,7 @@ dependencies:
11
11
  - pip # build
12
12
  - pytest-cov # tests
13
13
  - pytest # tests
14
+ - pandas # tests
14
15
  - polars # tests
15
16
  - pyarrow # tests
16
17
  - sphinx # docs
@@ -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.2.1"
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
- def test_anchor_boosting_second_order(gamma, objective, categorical_z):
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
- def test_compare_anchor_boosting_to_lgbm(parameters):
148
- X, y, a = simulate(f1, shift=0, seed=0)
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(X.to_arrow(), y, categorical_feature=[2]),
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=["x3"])
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(X.to_arrow())
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
- def test_refit(objective, seed, fun):
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)
@@ -1,7 +0,0 @@
1
- # Anchor Boosting
2
-
3
- Efficient tree-based nonlinear anchor regression and classification for Python [1]
4
-
5
- ## References
6
-
7
- [1] Londschien, M., M. Burger, G. Rätsch, P. Bühlmann (2025). Working paper.
File without changes
File without changes