geds-python 0.1.0a2__tar.gz → 0.1.0a3__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.
@@ -6,6 +6,13 @@ The project follows [Semantic Versioning](https://semver.org/). Versions use
6
6
  the Python packaging form of pre-release identifiers, such as `0.1.0a1` for
7
7
  the first alpha release.
8
8
 
9
+ ## 0.1.0a3 - 2026-09-23
10
+
11
+ - Add `python -m geds.check` for human-readable or JSON environment checks.
12
+ - Add `geds.plot_fit()` for Python-native visualization of univariate fits and
13
+ internal knots.
14
+ - Expand installation and R-library troubleshooting guidance.
15
+
9
16
  ## 0.1.0a2 - 2026-09-20
10
17
 
11
18
  - Preload R's core numerical DLLs on Windows so embedded R 4.6 can load
@@ -2,8 +2,8 @@ cff-version: 1.2.0
2
2
  message: "If you use GeDS for Python, please cite this software and the GeDS methodology references."
3
3
  title: "GeDS for Python"
4
4
  type: software
5
- version: 0.1.0a2
6
- date-released: 2026-09-20
5
+ version: 0.1.0a3
6
+ date-released: 2026-09-23
7
7
  license: GPL-3.0-only
8
8
  repository-code: "https://github.com/emilioluissaenzguillen/GeDS-python"
9
9
  authors:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: geds-python
3
- Version: 0.1.0a2
3
+ Version: 0.1.0a3
4
4
  Summary: Python estimators backed by the GeDS R package
5
5
  Project-URL: Homepage, https://github.com/emilioluissaenzguillen/GeDS-python
6
6
  Project-URL: Repository, https://github.com/emilioluissaenzguillen/GeDS-python
@@ -29,6 +29,7 @@ Requires-Dist: rpy2<3.7,>=3.6.7
29
29
  Requires-Dist: scikit-learn>=1.4
30
30
  Provides-Extra: dev
31
31
  Requires-Dist: build>=1.2; extra == 'dev'
32
+ Requires-Dist: matplotlib>=3.8; extra == 'dev'
32
33
  Requires-Dist: pytest>=8; extra == 'dev'
33
34
  Provides-Extra: plot
34
35
  Requires-Dist: matplotlib>=3.8; extra == 'plot'
@@ -75,12 +76,44 @@ selected R installation and package library through `geds.diagnostics()`.
75
76
 
76
77
  Check the backend before fitting:
77
78
 
79
+ ```console
80
+ python -m geds.check
81
+ ```
82
+
83
+ For a machine-readable report, use `python -m geds.check --json`. The same
84
+ information is available inside Python:
85
+
78
86
  ```python
79
87
  import geds
80
88
 
81
89
  print(geds.diagnostics())
82
90
  ```
83
91
 
92
+ ### Selecting R and its package library
93
+
94
+ Usually no configuration is necessary. If several R installations are
95
+ available, select one before starting Python:
96
+
97
+ ```powershell
98
+ $env:R_HOME = "C:\Program Files\R\R-4.6.1"
99
+ python -m geds.check
100
+ ```
101
+
102
+ ```bash
103
+ export R_HOME="/Library/Frameworks/R.framework/Resources" # macOS
104
+ # export R_HOME="/usr/lib/R" # Linux
105
+ python -m geds.check
106
+ ```
107
+
108
+ If GeDS is installed in a personal or otherwise non-default R library, set
109
+ `GEDS_R_LIBRARY` to the directory that contains the `GeDS` folder. You can
110
+ find that directory from R with `find.package("GeDS")`; use its parent
111
+ directory as `GEDS_R_LIBRARY`.
112
+
113
+ If the check reports that R is missing, install R or set `R_HOME`. If it finds
114
+ R but not GeDS, start that same R installation and run
115
+ `install.packages("GeDS")`, then rerun the check.
116
+
84
117
  ## Example
85
118
 
86
119
  Install the optional plotting dependency with
@@ -92,7 +125,7 @@ import matplotlib.pyplot as plt
92
125
  import numpy as np
93
126
  import pandas as pd
94
127
 
95
- from geds import GeDSRegressor
128
+ from geds import GeDSRegressor, plot_fit
96
129
 
97
130
  rng = np.random.RandomState(123)
98
131
  n = 500
@@ -108,15 +141,13 @@ y = rng.normal(means, scale=0.1)
108
141
  X = pd.DataFrame({"x": x})
109
142
 
110
143
  model = GeDSRegressor(order=3).fit(X, y)
111
- grid_x = np.linspace(x.min(), x.max(), 500)
112
- grid = pd.DataFrame({"x": grid_x})
113
- fitted = model.predict(grid)
114
144
  knots = np.asarray(model.knots_, dtype=float)
115
145
 
116
146
  print("Internal knots:", knots)
117
147
 
118
148
  fig, ax = plt.subplots()
119
- ax.scatter(x, y, s=12, alpha=0.35, label="Data")
149
+ plot_fit(model, X, y, ax=ax)
150
+ grid_x = np.linspace(x.min(), x.max(), 500)
120
151
  ax.plot(
121
152
  grid_x,
122
153
  f_1(grid_x),
@@ -125,16 +156,7 @@ ax.plot(
125
156
  linewidth=2,
126
157
  label="True mean",
127
158
  )
128
- ax.plot(grid_x, fitted, linewidth=2, label="GeDS fit")
129
- for index, knot in enumerate(knots):
130
- ax.axvline(
131
- knot,
132
- color="tab:red",
133
- linestyle="--",
134
- alpha=0.55,
135
- label="Internal knots" if index == 0 else None,
136
- )
137
- ax.set(xlabel="x", ylabel="y", title="GeDS spline regression")
159
+ ax.set(ylabel="y")
138
160
  ax.legend()
139
161
  fig.tight_layout()
140
162
  plt.show()
@@ -37,12 +37,44 @@ selected R installation and package library through `geds.diagnostics()`.
37
37
 
38
38
  Check the backend before fitting:
39
39
 
40
+ ```console
41
+ python -m geds.check
42
+ ```
43
+
44
+ For a machine-readable report, use `python -m geds.check --json`. The same
45
+ information is available inside Python:
46
+
40
47
  ```python
41
48
  import geds
42
49
 
43
50
  print(geds.diagnostics())
44
51
  ```
45
52
 
53
+ ### Selecting R and its package library
54
+
55
+ Usually no configuration is necessary. If several R installations are
56
+ available, select one before starting Python:
57
+
58
+ ```powershell
59
+ $env:R_HOME = "C:\Program Files\R\R-4.6.1"
60
+ python -m geds.check
61
+ ```
62
+
63
+ ```bash
64
+ export R_HOME="/Library/Frameworks/R.framework/Resources" # macOS
65
+ # export R_HOME="/usr/lib/R" # Linux
66
+ python -m geds.check
67
+ ```
68
+
69
+ If GeDS is installed in a personal or otherwise non-default R library, set
70
+ `GEDS_R_LIBRARY` to the directory that contains the `GeDS` folder. You can
71
+ find that directory from R with `find.package("GeDS")`; use its parent
72
+ directory as `GEDS_R_LIBRARY`.
73
+
74
+ If the check reports that R is missing, install R or set `R_HOME`. If it finds
75
+ R but not GeDS, start that same R installation and run
76
+ `install.packages("GeDS")`, then rerun the check.
77
+
46
78
  ## Example
47
79
 
48
80
  Install the optional plotting dependency with
@@ -54,7 +86,7 @@ import matplotlib.pyplot as plt
54
86
  import numpy as np
55
87
  import pandas as pd
56
88
 
57
- from geds import GeDSRegressor
89
+ from geds import GeDSRegressor, plot_fit
58
90
 
59
91
  rng = np.random.RandomState(123)
60
92
  n = 500
@@ -70,15 +102,13 @@ y = rng.normal(means, scale=0.1)
70
102
  X = pd.DataFrame({"x": x})
71
103
 
72
104
  model = GeDSRegressor(order=3).fit(X, y)
73
- grid_x = np.linspace(x.min(), x.max(), 500)
74
- grid = pd.DataFrame({"x": grid_x})
75
- fitted = model.predict(grid)
76
105
  knots = np.asarray(model.knots_, dtype=float)
77
106
 
78
107
  print("Internal knots:", knots)
79
108
 
80
109
  fig, ax = plt.subplots()
81
- ax.scatter(x, y, s=12, alpha=0.35, label="Data")
110
+ plot_fit(model, X, y, ax=ax)
111
+ grid_x = np.linspace(x.min(), x.max(), 500)
82
112
  ax.plot(
83
113
  grid_x,
84
114
  f_1(grid_x),
@@ -87,16 +117,7 @@ ax.plot(
87
117
  linewidth=2,
88
118
  label="True mean",
89
119
  )
90
- ax.plot(grid_x, fitted, linewidth=2, label="GeDS fit")
91
- for index, knot in enumerate(knots):
92
- ax.axvline(
93
- knot,
94
- color="tab:red",
95
- linestyle="--",
96
- alpha=0.55,
97
- label="Internal knots" if index == 0 else None,
98
- )
99
- ax.set(xlabel="x", ylabel="y", title="GeDS spline regression")
120
+ ax.set(ylabel="y")
100
121
  ax.legend()
101
122
  fig.tight_layout()
102
123
  plt.show()
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "geds-python"
7
- version = "0.1.0a2"
7
+ version = "0.1.0a3"
8
8
  description = "Python estimators backed by the GeDS R package"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -42,7 +42,7 @@ dependencies = [
42
42
 
43
43
  [project.optional-dependencies]
44
44
  test = ["pytest>=8"]
45
- dev = ["build>=1.2", "pytest>=8"]
45
+ dev = ["build>=1.2", "matplotlib>=3.8", "pytest>=8"]
46
46
  plot = ["matplotlib>=3.8"]
47
47
 
48
48
  [project.urls]
@@ -2,12 +2,14 @@
2
2
 
3
3
  from ._backend import BackendUnavailableError, diagnostics
4
4
  from ._estimators import GeDSGeneralizedRegressor, GeDSRegressor
5
+ from ._plotting import plot_fit
5
6
 
6
7
  __all__ = [
7
8
  "BackendUnavailableError",
8
9
  "GeDSGeneralizedRegressor",
9
10
  "GeDSRegressor",
10
11
  "diagnostics",
12
+ "plot_fit",
11
13
  ]
12
14
 
13
- __version__ = "0.1.0a2"
15
+ __version__ = "0.1.0a3"
@@ -0,0 +1,69 @@
1
+ """Optional Python-native plotting helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+ from sklearn.utils.validation import check_is_fitted
10
+
11
+
12
+ def plot_fit(
13
+ estimator: Any,
14
+ X: Any,
15
+ y: Any | None = None,
16
+ *,
17
+ ax: Any | None = None,
18
+ grid_size: int = 500,
19
+ show_knots: bool = True,
20
+ ) -> Any:
21
+ """Plot a fitted univariate GeDS model and return its Matplotlib axes.
22
+
23
+ This helper only visualizes predictions already produced by GeDS; it does
24
+ not implement any statistical calculation in Python.
25
+ """
26
+ try:
27
+ import matplotlib.pyplot as plt
28
+ except ImportError as exc: # pragma: no cover - depends on optional extra
29
+ raise ImportError(
30
+ 'Plotting requires Matplotlib; install "geds-python[plot]".'
31
+ ) from exc
32
+
33
+ check_is_fitted(estimator, "_r_model_")
34
+ frame, named_input = estimator._frame(X)
35
+ if frame.shape[1] != 1 or estimator.n_features_in_ != 1:
36
+ raise ValueError("plot_fit supports fitted models with one feature only.")
37
+ if grid_size < 2:
38
+ raise ValueError("grid_size must be at least 2.")
39
+ values = np.asarray(frame.iloc[:, 0], dtype=float)
40
+ if not np.isfinite(values).all():
41
+ raise ValueError("X must contain only finite values.")
42
+ grid_values = np.linspace(values.min(), values.max(), grid_size)
43
+ if named_input:
44
+ grid = pd.DataFrame({frame.columns[0]: grid_values})
45
+ else:
46
+ grid = grid_values.reshape(-1, 1)
47
+ fitted = estimator.predict(grid)
48
+
49
+ if ax is None:
50
+ _, ax = plt.subplots()
51
+ if y is not None:
52
+ response = np.asarray(y, dtype=float)
53
+ if response.ndim != 1 or len(response) != len(values):
54
+ raise ValueError("y must be one-dimensional and have the same length as X.")
55
+ ax.scatter(values, response, s=12, alpha=0.35, label="Data")
56
+ ax.plot(grid_values, fitted, linewidth=2, label="GeDS fit")
57
+ if show_knots and estimator.knots_ is not None:
58
+ knots = np.asarray(estimator.knots_, dtype=float).ravel()
59
+ for index, knot in enumerate(knots):
60
+ ax.axvline(
61
+ knot,
62
+ color="tab:red",
63
+ linestyle="--",
64
+ alpha=0.55,
65
+ label="Internal knots" if index == 0 else None,
66
+ )
67
+ ax.set(xlabel=str(frame.columns[0]), ylabel="Response", title="GeDS spline regression")
68
+ ax.legend()
69
+ return ax
@@ -0,0 +1,64 @@
1
+ """Command-line environment check for the Python/R bridge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from typing import Sequence
9
+
10
+ from ._backend import BackendUnavailableError, diagnostics
11
+
12
+
13
+ def _parser() -> argparse.ArgumentParser:
14
+ parser = argparse.ArgumentParser(
15
+ prog="python -m geds.check",
16
+ description="Check that Python can initialize R and load the GeDS package.",
17
+ )
18
+ parser.add_argument(
19
+ "--json",
20
+ action="store_true",
21
+ help="print machine-readable JSON instead of the human-readable report",
22
+ )
23
+ return parser
24
+
25
+
26
+ def main(argv: Sequence[str] | None = None) -> int:
27
+ """Run the environment check and return a process exit code."""
28
+ args = _parser().parse_args(argv)
29
+ try:
30
+ information = diagnostics()
31
+ except BackendUnavailableError as exc:
32
+ if args.json:
33
+ print(json.dumps({"status": "error", "message": str(exc)}, indent=2))
34
+ else:
35
+ print("GeDS environment check: FAILED", file=sys.stderr)
36
+ print(str(exc), file=sys.stderr)
37
+ print(
38
+ "Set R_HOME if the intended R installation is not discovered, "
39
+ "and set GEDS_R_LIBRARY if GeDS is in a non-default R library.",
40
+ file=sys.stderr,
41
+ )
42
+ return 1
43
+
44
+ report = {"status": "ok", **information}
45
+ if args.json:
46
+ print(json.dumps(report, indent=2, sort_keys=True))
47
+ else:
48
+ labels = {
49
+ "python": "Python",
50
+ "r_version": "R",
51
+ "r_home": "R home",
52
+ "rpy2_version": "rpy2",
53
+ "geds_version": "GeDS",
54
+ "geds_library": "GeDS library",
55
+ "minimum_geds_version": "Minimum GeDS",
56
+ }
57
+ print("GeDS environment check: OK")
58
+ for key, label in labels.items():
59
+ print(f" {label}: {information[key]}")
60
+ return 0
61
+
62
+
63
+ if __name__ == "__main__": # pragma: no cover - exercised as a module
64
+ raise SystemExit(main())
@@ -9,7 +9,9 @@ import pytest
9
9
  from sklearn.base import clone, is_regressor
10
10
 
11
11
  import geds
12
- from geds import GeDSGeneralizedRegressor, GeDSRegressor
12
+ import geds.check as check_module
13
+ from geds import GeDSGeneralizedRegressor, GeDSRegressor, plot_fit
14
+ from geds.check import main as check_main
13
15
 
14
16
 
15
17
  def test_distribution_and_module_versions_match():
@@ -31,6 +33,29 @@ def test_diagnostics():
31
33
  assert info["minimum_geds_version"] == "0.3.6"
32
34
 
33
35
 
36
+ def test_environment_check(capsys):
37
+ assert check_main([]) == 0
38
+ assert "GeDS environment check: OK" in capsys.readouterr().out
39
+
40
+
41
+ def test_environment_check_json(capsys):
42
+ assert check_main(["--json"]) == 0
43
+ output = capsys.readouterr().out
44
+ assert '"status": "ok"' in output
45
+ assert '"geds_version"' in output
46
+
47
+
48
+ def test_environment_check_failure(monkeypatch, capsys):
49
+ def unavailable():
50
+ raise geds.BackendUnavailableError("R was not found.")
51
+
52
+ monkeypatch.setattr(check_module, "diagnostics", unavailable)
53
+ assert check_main([]) == 1
54
+ error = capsys.readouterr().err
55
+ assert "GeDS environment check: FAILED" in error
56
+ assert "R_HOME" in error
57
+
58
+
34
59
  def test_ngeds_reference_values_and_pickle(tmp_path):
35
60
  frame = _r_reference_frame(
36
61
  """
@@ -60,6 +85,10 @@ def test_ngeds_reference_values_and_pickle(tmp_path):
60
85
  loaded = GeDSRegressor.load(model_path)
61
86
  np.testing.assert_array_equal(loaded.predict(frame[["X"]].iloc[:5]), prediction)
62
87
 
88
+ axes = plot_fit(estimator, frame[["X"]], frame["Y"], grid_size=50)
89
+ assert axes.get_title() == "GeDS spline regression"
90
+ assert len(axes.lines) == len(np.asarray(estimator.knots_).ravel()) + 1
91
+
63
92
 
64
93
  def test_ggeds_poisson_reference_values():
65
94
  frame = _r_reference_frame(
File without changes
File without changes