quantile-forest 1.3.2__tar.gz → 1.3.4__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 (58) hide show
  1. {quantile-forest-1.3.2/quantile_forest.egg-info → quantile-forest-1.3.4}/PKG-INFO +2 -2
  2. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/README.md +1 -1
  3. quantile-forest-1.3.4/docs/_static/favicon.ico +0 -0
  4. quantile-forest-1.3.4/docs/_static/quantile-forest-logo-white.png +0 -0
  5. quantile-forest-1.3.4/docs/_static/quantile-forest-logo.png +0 -0
  6. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/conf.py +17 -3
  7. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/index.rst +5 -5
  8. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/install.rst +1 -0
  9. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinxext/gallery.py +8 -16
  10. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/user_guide.rst +16 -9
  11. {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_conformalized.py +24 -22
  12. {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_example.py +43 -57
  13. {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_extrapolation.py +23 -44
  14. {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_interpolation.py +48 -59
  15. {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_intervals.py +15 -29
  16. {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_multioutput.py +20 -16
  17. quantile-forest-1.3.4/examples/plot_quantile_vs_standard.py +94 -0
  18. quantile-forest-1.3.4/examples/plot_quantile_weighting.py +177 -0
  19. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/_quantile_forest.py +26 -25
  20. quantile-forest-1.3.4/quantile_forest/_quantile_forest_fast.pxd +47 -0
  21. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/_quantile_forest_fast.pyx +78 -80
  22. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/tests/test_quantile_forest.py +32 -10
  23. quantile-forest-1.3.4/quantile_forest/version.py +1 -0
  24. quantile-forest-1.3.4/quantile_forest/version.txt +1 -0
  25. {quantile-forest-1.3.2 → quantile-forest-1.3.4/quantile_forest.egg-info}/PKG-INFO +2 -2
  26. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/SOURCES.txt +13 -10
  27. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/top_level.txt +1 -0
  28. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/setup.py +12 -1
  29. quantile-forest-1.3.2/quantile_forest/_quantile_forest_fast.pxd +0 -48
  30. quantile-forest-1.3.2/quantile_forest/tests/examples/plot_quantile_vs_standard.py +0 -93
  31. quantile-forest-1.3.2/quantile_forest/tests/examples/plot_quantile_weighting.py +0 -152
  32. quantile-forest-1.3.2/quantile_forest/version.py +0 -1
  33. quantile-forest-1.3.2/quantile_forest/version.txt +0 -1
  34. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/LICENSE +0 -0
  35. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/MANIFEST.in +0 -0
  36. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/Makefile +0 -0
  37. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_static/css/gallery.css +0 -0
  38. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_static/css/theme_overrides.css +0 -0
  39. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_static/js/copybutton.js +0 -0
  40. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_templates/class.rst +0 -0
  41. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_templates/navbar-project.html +0 -0
  42. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_templates/sidebar-logo.html +0 -0
  43. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/api.rst +0 -0
  44. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/make.bat +0 -0
  45. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/references.rst +0 -0
  46. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/refs.bib +0 -0
  47. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinx_requirements.txt +0 -0
  48. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinxext/__init__.py +0 -0
  49. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinxext/utils.py +1 -1
  50. {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/__init__.py +0 -0
  51. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/pyproject.toml +0 -0
  52. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/__init__.py +0 -0
  53. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/_min_dependencies.py +0 -0
  54. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/tests/__init__.py +0 -0
  55. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/dependency_links.txt +0 -0
  56. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/not-zip-safe +0 -0
  57. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/requires.txt +0 -0
  58. {quantile-forest-1.3.2 → quantile-forest-1.3.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantile-forest
3
- Version: 1.3.2
3
+ Version: 1.3.4
4
4
  Summary: scikit-learn compatible quantile forests.
5
5
  Home-page: https://zillow.github.io/quantile-forest
6
6
  Download-URL: https://pypi.org/project/quantile-forest/#files
@@ -31,7 +31,7 @@ Requires-Dist: numpy>=1.23
31
31
  Requires-Dist: scipy>=1.4
32
32
  Requires-Dist: scikit-learn>=1.0
33
33
 
34
- # quantile-forest
34
+ # quantile-forest <a href="https://zillow.github.io/quantile-forest/"><img align="right" src="https://zillow.github.io/quantile-forest/_static/quantile-forest-logo.png" height="50"></img></a>
35
35
 
36
36
  [![PyPI - Version](https://img.shields.io/pypi/v/quantile-forest)](https://pypi.org/project/quantile-forest)
37
37
  [![License](https://img.shields.io/github/license/zillow/quantile-forest)](https://github.com/zillow/quantile-forest/blob/main/LICENSE)
@@ -1,4 +1,4 @@
1
- # quantile-forest
1
+ # quantile-forest <a href="https://zillow.github.io/quantile-forest/"><img align="right" src="https://zillow.github.io/quantile-forest/_static/quantile-forest-logo.png" height="50"></img></a>
2
2
 
3
3
  [![PyPI - Version](https://img.shields.io/pypi/v/quantile-forest)](https://pypi.org/project/quantile-forest)
4
4
  [![License](https://img.shields.io/github/license/zillow/quantile-forest)](https://github.com/zillow/quantile-forest/blob/main/LICENSE)
@@ -16,6 +16,7 @@ import sys
16
16
  from datetime import datetime
17
17
 
18
18
  sys.path.insert(0, os.path.abspath("."))
19
+ sys.path.insert(0, os.path.abspath(".."))
19
20
 
20
21
  # If extensions (or modules to document with autodoc) are in another directory,
21
22
  # add these directories to sys.path here. If the directory is relative to the
@@ -104,11 +105,12 @@ html_theme = "pydata_sphinx_theme"
104
105
 
105
106
  html_theme_options = {
106
107
  # "navbar_start": ["navbar-logo", "navbar-project"],
107
- "navbar_start": ["navbar-project"],
108
+ "navbar_start": ["navbar-logo", "navbar-project"],
108
109
  "navbar_center": ["navbar-nav"],
109
110
  "navbar_end": ["theme-switcher", "navbar-icon-links"],
110
111
  "primary_sidebar_end": [],
111
112
  "footer_items": [],
113
+ "logo": {"image_dark": "_static/quantile-forest-logo-white.png"},
112
114
  "icon_links": [
113
115
  {
114
116
  "name": "GitHub",
@@ -124,11 +126,20 @@ html_context = {"default_mode": "light"}
124
126
  # Add any paths that contain custom static files (such as style sheets) here,
125
127
  # relative to this directory. They are copied after the builtin static files,
126
128
  # so a file named "default.css" will overwrite the builtin "default.css".
127
- html_static_path = ["_static"]
129
+ html_static_path = ["_static", "_images"]
128
130
 
129
131
  # Output file base name for HTML help builder.
130
132
  html_short_title = "quantile-forest"
131
133
 
134
+ # The name of an image file (relative to this directory) to place at the top
135
+ # of the sidebar.
136
+ html_logo = "_static/quantile-forest-logo.png"
137
+
138
+ # The name of an image file (relative to this directory) to use as a favicon of
139
+ # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
140
+ # pixels large.
141
+ html_favicon = "_static/favicon.ico"
142
+
132
143
  htmlhelp_basename = "quantile-forestdoc"
133
144
 
134
145
 
@@ -150,7 +161,10 @@ html_sidebars = {
150
161
 
151
162
  # -- Options for autodoc ------------------------------------------------------
152
163
 
153
- autodoc_default_flags = ["members", "inherited-members"]
164
+ autodoc_default_options = {
165
+ "members": True,
166
+ "inherited-members": True,
167
+ }
154
168
 
155
169
  # Generate autosummary even if no references.
156
170
  autosummary_generate = True
@@ -1,7 +1,7 @@
1
1
  :html_theme.sidebar_secondary.remove:
2
2
 
3
- quantile-forest Documentation
4
- =============================
3
+ quantile-forest
4
+ ===============
5
5
  .. role:: raw-html(raw)
6
6
  :format: html
7
7
 
@@ -23,21 +23,21 @@ quantile-forest Documentation
23
23
  :link-type: ref
24
24
  :link-alt: Getting started
25
25
 
26
- A guide that provides installation instructions and information on testing the package.
26
+ A guide that provides installation requirements and instructions, as well as procedures for developers.
27
27
 
28
28
  .. grid-item-card:: User Guide
29
29
  :link: user_guide
30
30
  :link-type: ref
31
31
  :link-alt: User guide
32
32
 
33
- Check out the User Guide for information on the key concepts behind quantile forests.
33
+ Information on the key concepts behind quantile forests and how they apply to this package.
34
34
 
35
35
  .. grid-item-card:: Examples
36
36
  :link: example-gallery
37
37
  :link-type: ref
38
38
  :link-alt: Examples
39
39
 
40
- General-purpose, introductory and illustrative examples of using quantile forests.
40
+ Examples that demonstrate the broad applications and introductory concepts of quantile forests.
41
41
 
42
42
  .. grid-item-card:: API
43
43
  :link: api
@@ -59,6 +59,7 @@ Documentation
59
59
  To build the documentation, run::
60
60
 
61
61
  $ pip install -r ./docs/sphinx_requirements.txt
62
+ $ mkdir -p ./docs/_images
62
63
  $ sphinx-build -b html ./docs ./docs/_build
63
64
 
64
65
  .. toctree::
@@ -1,31 +1,24 @@
1
1
  """Adapted from Vega-Altair. BSD-3-Clause License. https://github.com/altair-viz/altair"""
2
2
 
3
3
  import hashlib
4
- import os
5
4
  import json
5
+ import os
6
6
  import random
7
- from operator import itemgetter
8
- import warnings
9
7
  import shutil
8
+ import warnings
9
+ from operator import itemgetter
10
10
 
11
11
  import jinja2
12
-
12
+ from altair.utils.execeval import eval_block
13
13
  from docutils import nodes
14
- from docutils.statemachine import ViewList
15
14
  from docutils.parsers.rst import Directive
16
15
  from docutils.parsers.rst.directives import flag
17
-
16
+ from docutils.statemachine import ViewList
18
17
  from sphinx.util.nodes import nested_parse_with_titles
19
18
 
20
- from .utils import (
21
- get_docstring_and_rest,
22
- prev_this_next,
23
- create_thumbnail,
24
- create_generic_image,
25
- )
26
- from altair.utils.execeval import eval_block
19
+ from examples import iter_examples
27
20
 
28
- from quantile_forest.tests.examples import iter_examples
21
+ from .utils import create_generic_image, create_thumbnail, get_docstring_and_rest, prev_this_next
29
22
 
30
23
  EXAMPLE_MODULE = "examples"
31
24
 
@@ -257,8 +250,7 @@ class MiniGalleryDirective(Directive):
257
250
  def main(app):
258
251
  gallery_dir = app.builder.config.gallery_dir
259
252
  target_dir = os.path.join(app.builder.srcdir, gallery_dir)
260
- # image_dir = os.path.join(app.builder.srcdir, "_images")
261
- image_dir = os.path.join(app.builder.srcdir, "_static")
253
+ image_dir = os.path.join(app.builder.srcdir, "_images")
262
254
 
263
255
  gallery_ref = app.builder.config.gallery_ref
264
256
  gallery_title = app.builder.config.gallery_title
@@ -75,6 +75,10 @@ Default quantiles can be specified at model initialization using the `default_qu
75
75
  >>> reg.fit(X_train, y_train)
76
76
  RandomForestQuantileRegressor(default_quantiles=[0.25, 0.5, 0.75])
77
77
  >>> y_pred = reg.predict(X_test) # predicts using the default quantiles
78
+ >>> y_pred.ndim == 2
79
+ True
80
+ >>> y_pred.shape[1] == 3
81
+ True
78
82
 
79
83
  The default quantiles can be overwritten at prediction time by specifying a value for `quantiles`:
80
84
 
@@ -82,6 +86,8 @@ The default quantiles can be overwritten at prediction time by specifying a valu
82
86
  >>> reg.fit(X_train, y_train)
83
87
  RandomForestQuantileRegressor(default_quantiles=[0.25, 0.5, 0.75])
84
88
  >>> y_pred = reg.predict(X_test, quantiles=0.5) # uses override quantiles
89
+ >>> y_pred.ndim == 1
90
+ True
85
91
 
86
92
  The output of the `predict` method is an array with one column for each specified quantile or a single column if no quantiles are specified. The order of the output columns corresponds to the order of the quantiles, which can be specified in any order (i.e., they do not need to be monotonically ordered)::
87
93
 
@@ -89,7 +95,7 @@ The output of the `predict` method is an array with one column for each specifie
89
95
  >>> (y_pred[:, 0] >= y_pred[:, 1]).all()
90
96
  True
91
97
 
92
- Multi-target quantile regression is also supported. If the target values are multi-dimensional, then the final output column will correspond to the number of targets::
98
+ Multi-target quantile regression is also supported. If the target values are multi-dimensional, then the second output column will correspond to the number of targets::
93
99
 
94
100
  >>> from sklearn import datasets
95
101
  >>> from quantile_forest import RandomForestQuantileRegressor
@@ -105,25 +111,24 @@ Multi-target quantile regression is also supported. If the target values are mul
105
111
  True
106
112
  >>> y_pred.shape[0] == len(X)
107
113
  True
108
- >>> y_pred.shape[1] == len(quantiles)
114
+ >>> y_pred.shape[-1] == len(quantiles)
109
115
  True
110
- >>> y_pred.shape[-1] == y.shape[1]
116
+ >>> y_pred.shape[1] == y.shape[1]
111
117
  True
112
118
 
113
119
  Quantile Weighting
114
120
  ~~~~~~~~~~~~~~~~~~
115
121
 
116
- By default, the predict method calculates quantiles by weighting each sample inversely according to the size of its leaf node (`weighted_leaves = True`). If `weighted_leaves = False`, each sample in a leaf (including repeated bootstrap samples) will be given equal weight. Note that this leaf-based weighting can only be used with weighted quantiles.
117
-
118
122
  By default, the predict method calculates quantiles using a weighted quantile method (`weighted_quantile = True`), which assigns a weight to each sample in the training set based on the number of times that it co-occurs in the same leaves as the test sample. When the number of samples in the training set is larger than the expected size of this list (i.e., :math:`n_{train} \gg n_{trees} \cdot n_{leaves} \cdot n_{leafsamples}`), it can be more efficient to calculate an unweighted quantile (`weighted_quantile = False`), which aggregates the list of training `y` values for each leaf node to which the test sample belongs across all trees. For a given input, both methods can return the same output values::
119
123
 
120
124
  >>> import numpy as np
121
- >>> kwargs = {"weighted_leaves": False}
122
- >>> y_pred_weighted = reg.predict(X_test, weighted_quantile=True, **kwargs)
123
- >>> y_pred_unweighted = reg.predict(X_test, weighted_quantile=False, **kwargs)
125
+ >>> y_pred_weighted = reg.predict(X_test, weighted_quantile=True)
126
+ >>> y_pred_unweighted = reg.predict(X_test, weighted_quantile=False)
124
127
  >>> np.allclose(y_pred_weighted, y_pred_unweighted)
125
128
  True
126
129
 
130
+ By default, the predict method calculates quantiles by giving each sample in a leaf (including repeated bootstrap samples) equal weight (`weighted_leaves = False`). If `weighted_leaves = True`, each sample will be weighted inversely according to the size of its leaf node. Note that this leaf-based weighting can only be used with weighted quantiles.
131
+
127
132
  Out-of-Bag Estimation
128
133
  ~~~~~~~~~~~~~~~~~~~~~
129
134
 
@@ -195,13 +200,15 @@ Proximity counts are counts of the number of times that two samples share a leaf
195
200
  >>> X, y = datasets.load_diabetes(return_X_y=True)
196
201
  >>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
197
202
  >>> reg = RandomForestQuantileRegressor().fit(X_train, y_train)
198
- >>> proximities = reg.proximity_counts(X_test)
203
+ >>> proximities = reg.proximity_counts(X_test) # proximity counts for X_test
199
204
 
200
205
  For each test sample, the method outputs a list of tuples of the training index and proximity count, listed in descending order by proximity count. For example, a test sample with an output of [(1, 5), (0, 3), (3, 1)], means that the test sample shared 5, 3, and 1 leaf nodes with the training samples that were (zero-)indexed as 1, 0, and 3 during model fitting, respectively.
201
206
 
202
207
  The maximum number of proximity counts output per test sample can be limited by specifying `max_proximities`::
203
208
 
204
209
  >>> proximities = reg.proximity_counts(X_test, max_proximities=10)
210
+ >>> np.all([len(prox) <= 10 for prox in proximities])
211
+ True
205
212
 
206
213
  Out-of-bag (OOB) proximity counts can be returned by specifying `oob_score = True`::
207
214
 
@@ -6,8 +6,10 @@ An example that demonstrates the use of a quantile regression forest (QRF) to
6
6
  construct reliable prediction intervals using conformalized quantile
7
7
  regression (CQR). CQR offers prediction intervals that attain valid coverage,
8
8
  while QRF may require additional calibration for reliable interval estimates.
9
- Adapted from "Prediction intervals: Quantile Regression Forests" by Carl McBride
10
- Ellis:
9
+ Notice that in this example, by using CQR we obtain a level of coverage (i.e.,
10
+ percentage of samples that actaully fall within their prediction interval)
11
+ that is closer to the target level. Adapted from "Prediction intervals:
12
+ Quantile Regression Forests" by Carl McBride Ellis:
11
13
  https://www.kaggle.com/code/carlmcbrideellis/prediction-intervals-quantile-regression-forests.
12
14
  """
13
15
 
@@ -79,7 +81,9 @@ def qrf_strategy(alpha, X_train, X_test, y_train, y_test):
79
81
  # Calculate the point predictions on the test data.
80
82
  y_pred = qrf.predict(X_test, quantiles="mean", aggregate_leaves_first=False)
81
83
 
82
- return sort_y_values(y_test, y_pred, y_pis)
84
+ y_values = sort_y_values(y_test, y_pred, y_pis)
85
+
86
+ return pd.DataFrame(y_values).pipe(lambda x: x * 100_000).assign(strategy="qrf")
83
87
 
84
88
 
85
89
  def cqr_strategy(alpha, X_train, X_test, y_train, y_test):
@@ -119,32 +123,32 @@ def cqr_strategy(alpha, X_train, X_test, y_train, y_test):
119
123
  # Calculate the point predictions on the test data.
120
124
  y_pred = qrf.predict(X_test, quantiles="mean", aggregate_leaves_first=False)
121
125
 
122
- return sort_y_values(y_test, y_pred, y_pis)
126
+ y_values = sort_y_values(y_test, y_pred, y_pis)
127
+
128
+ return pd.DataFrame(y_values).pipe(lambda x: x * 100_000).assign(strategy="cqr")
123
129
 
124
130
 
125
131
  # Get strategy outputs as a data frame.
126
132
  args = (alpha, X_train, X_test, y_train, y_test)
127
- df = pd.concat(
128
- [
129
- pd.DataFrame(qrf_strategy(*args)).pipe(lambda x: x * 100_000).assign(strategy="qrf"),
130
- pd.DataFrame(cqr_strategy(*args)).pipe(lambda x: x * 100_000).assign(strategy="cqr"),
131
- ]
132
- )
133
+ df = pd.concat([qrf_strategy(*args), cqr_strategy(*args)])
133
134
 
134
- # Add coverage and mean width metrics to the data frame.
135
- df = df.merge(
135
+ # Calculate coverage and width metrics.
136
+ metrics = (
136
137
  df.groupby("strategy")
137
138
  .apply(
138
- lambda x: pd.Series(
139
+ lambda grp: pd.Series(
139
140
  {
140
- "coverage": coverage_score(x["y_test"], x["y_pred_low"], x["y_pred_upp"]),
141
- "width": mean_width_score(x["y_pred_low"], x["y_pred_upp"]),
141
+ "coverage": coverage_score(grp["y_test"], grp["y_pred_low"], grp["y_pred_upp"]),
142
+ "width": mean_width_score(grp["y_pred_low"], grp["y_pred_upp"]),
142
143
  }
143
144
  )
144
145
  )
145
146
  .reset_index()
146
147
  )
147
148
 
149
+ # Merge the metrics into the data frame.
150
+ df = df.merge(metrics, on="strategy", how="left")
151
+
148
152
 
149
153
  def plot_prediction_intervals(df, domain):
150
154
  click = alt.selection_point(fields=["y_label"], bind="legend")
@@ -220,12 +224,10 @@ def plot_prediction_intervals(df, domain):
220
224
  )
221
225
 
222
226
  text_coverage = (
223
- base.transform_aggregate(
224
- coverage="mean(coverage)", width="mean(width)", groupby=["strategy"]
225
- )
227
+ base.transform_aggregate(coverage="mean(coverage)", groupby=["strategy"])
226
228
  .transform_calculate(
227
229
  coverage_text=(
228
- "'Coverage: ' + format(datum.coverage * 100, '.1f') + '%'"
230
+ f"'Coverage: ' + format({alt.datum['coverage'] * 100}, '.1f') + '%'"
229
231
  f" + ' (target = {cov_pct}%)'"
230
232
  )
231
233
  )
@@ -237,10 +239,10 @@ def plot_prediction_intervals(df, domain):
237
239
  )
238
240
  )
239
241
  text_with = (
240
- base.transform_aggregate(
241
- coverage="mean(coverage)", width="mean(width)", groupby=["strategy"]
242
+ base.transform_aggregate(width="mean(width)", groupby=["strategy"])
243
+ .transform_calculate(
244
+ width_text=f"'Interval Width: ' + format({alt.datum['width']}, '$,d')"
242
245
  )
243
- .transform_calculate(width_text="'Interval Width: ' + format(datum.width, '$,d')")
244
246
  .mark_text(align="left", baseline="top")
245
247
  .encode(
246
248
  x=alt.value(5),
@@ -8,17 +8,18 @@ the predictions to a ground truth function used to generate noisy samples.
8
8
  """
9
9
 
10
10
  import altair as alt
11
- import pandas as pd
12
11
  import numpy as np
12
+ import pandas as pd
13
13
  from sklearn.model_selection import train_test_split
14
14
 
15
15
  from quantile_forest import RandomForestQuantileRegressor
16
16
 
17
17
  n_samples = 1000
18
18
  bounds = [0, 10]
19
+ quantiles = [0.025, 0.5, 0.975]
19
20
 
20
21
 
21
- def make_toy_dataset(n_samples, bounds, random_seed=0):
22
+ def make_toy_dataset(n_samples, bounds, add_noise=True, random_seed=0):
22
23
  rng = np.random.RandomState(random_seed)
23
24
 
24
25
  x = rng.uniform(*bounds, size=n_samples)
@@ -26,109 +27,94 @@ def make_toy_dataset(n_samples, bounds, random_seed=0):
26
27
 
27
28
  sigma = 0.25 + x / 10
28
29
  noise = rng.lognormal(sigma=sigma) - np.exp(sigma**2 / 2)
29
- y = f + noise
30
+ y = f + (noise if add_noise else 0)
30
31
 
31
32
  return np.atleast_2d(x).T, y
32
33
 
33
34
 
34
- X, y = make_toy_dataset(n_samples, bounds)
35
+ # Create noisy data for modeling and non-noisy function data for illustration.
36
+ X, y = make_toy_dataset(n_samples, bounds, add_noise=True, random_seed=0)
37
+ X_func, y_func = make_toy_dataset(n_samples, bounds, add_noise=False, random_seed=0)
35
38
 
36
39
  X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
37
40
 
38
- X_sampled = np.atleast_2d(np.linspace(*bounds, n_samples)).T
39
- y_sampled = (X_sampled * np.sin(X_sampled)).reshape(-1)
40
-
41
41
  qrf = RandomForestQuantileRegressor(max_depth=3, min_samples_leaf=5, random_state=0)
42
42
  qrf.fit(X_train, y_train)
43
43
 
44
- y_pred = qrf.predict(X_sampled, quantiles=[0.025, 0.5, 0.975])
44
+ y_pred_func = qrf.predict(X_func, quantiles=quantiles)
45
+ y_pred_test = qrf.predict(X_test, quantiles=quantiles)
45
46
 
46
- df_train = pd.DataFrame(
47
+ df = pd.DataFrame(
47
48
  {
48
- "X_sampled": X_sampled.reshape(-1),
49
- "y_sampled": y_sampled,
50
- "y_pred_low": y_pred[:, 0],
51
- "y_pred_med": y_pred[:, 1],
52
- "y_pred_upp": y_pred[:, 2],
49
+ "X": np.concatenate([X_func.reshape(-1), X_test.reshape(-1)]),
50
+ "y": np.concatenate([y_func, y_test]),
51
+ "y_pred": np.concatenate([y_pred_func[:, 1], y_pred_test[:, 1]]),
52
+ "y_pred_low": np.concatenate([y_pred_func[:, 0], y_pred_test[:, 0]]),
53
+ "y_pred_upp": np.concatenate([y_pred_func[:, 2], y_pred_test[:, 2]]),
54
+ "test": [False] * len(y_func) + [True] * len(y_test),
53
55
  }
54
56
  )
55
57
 
56
- df_test = pd.DataFrame(
57
- {
58
- "X_test": X_test.reshape(-1),
59
- "y_test": y_test,
60
- }
61
- )
62
-
63
-
64
- def plot_fit_and_intervals(df_train, df_test):
65
- df_train = df_train.copy()
66
- df_test = df_test.copy()
67
-
68
- df_train = df_train.assign(
69
- **{
70
- "y_true_label": "f(x) = x sin(x)",
71
- "y_pred_label": "Predicted Median",
72
- "y_area_label": "Predicted 95% Interval",
73
- }
74
- )
75
-
76
- df_test["point_label"] = "Test Observations"
77
58
 
59
+ def plot_fit_and_intervals(df):
78
60
  points = (
79
- alt.Chart(df_test)
61
+ alt.Chart(df.assign(**{"point_label": "Test Observations"}))
62
+ .transform_filter(alt.datum["test"]) # filter to test data
80
63
  .mark_circle(color="#f2a619")
81
64
  .encode(
82
- x=alt.X("X_test:Q", scale=alt.Scale(nice=False)),
83
- y=alt.Y("y_test:Q", title=""),
65
+ x=alt.X("X:Q", scale=alt.Scale(nice=False)),
66
+ y=alt.Y("y:Q", title=""),
84
67
  color=alt.Color("point_label:N", scale=alt.Scale(range=["#f2a619"]), title=None),
85
68
  tooltip=[
86
- alt.Tooltip("X_test:Q", format=",.3f", title="X"),
87
- alt.Tooltip("y_test:Q", format=",.3f", title="Y"),
69
+ alt.Tooltip("X:Q", format=",.3f", title="X"),
70
+ alt.Tooltip("y:Q", format=",.3f", title="Y"),
88
71
  ],
89
72
  )
90
73
  )
91
74
 
92
75
  line_true = (
93
- alt.Chart(df_train)
76
+ alt.Chart(df.assign(**{"y_true_label": "f(x) = x sin(x)"}))
77
+ .transform_filter(~alt.datum["test"]) # filter to training data
94
78
  .mark_line(color="black", size=3)
95
79
  .encode(
96
- x=alt.X("X_sampled:Q", scale=alt.Scale(nice=False)),
97
- y=alt.Y("y_sampled:Q", title="f(x)"),
80
+ x=alt.X("X:Q", scale=alt.Scale(nice=False)),
81
+ y=alt.Y("y:Q", title="f(x)"),
98
82
  color=alt.Color("y_true_label:N", scale=alt.Scale(range=["black"]), title=None),
99
83
  tooltip=[
100
- alt.Tooltip("X_sampled:Q", format=",.3f", title="X"),
101
- alt.Tooltip("y_sampled:Q", format=",.3f", title="Y"),
84
+ alt.Tooltip("X:Q", format=",.3f", title="X"),
85
+ alt.Tooltip("y:Q", format=",.3f", title="Y"),
102
86
  ],
103
87
  )
104
88
  )
105
89
 
106
90
  line_pred = (
107
- alt.Chart(df_train)
91
+ alt.Chart(df.assign(**{"y_pred_label": "Predicted Median"}))
92
+ .transform_filter(~alt.datum["test"]) # filter to training data
108
93
  .mark_line(color="#006aff", size=5)
109
94
  .encode(
110
- x=alt.X("X_sampled:Q", scale=alt.Scale(nice=False)),
111
- y=alt.Y("y_pred_med:Q", title=""),
95
+ x=alt.X("X:Q", scale=alt.Scale(nice=False)),
96
+ y=alt.Y("y_pred:Q", title=""),
112
97
  color=alt.Color("y_pred_label:N", scale=alt.Scale(range=["#006aff"]), title=None),
113
98
  tooltip=[
114
- alt.Tooltip("X_sampled:Q", format=",.3f", title="X"),
115
- alt.Tooltip("y_sampled:Q", format=",.3f", title="Y"),
116
- alt.Tooltip("y_pred_med:Q", format=",.3f", title="Predicted Y"),
99
+ alt.Tooltip("X:Q", format=",.3f", title="X"),
100
+ alt.Tooltip("y:Q", format=",.3f", title="Y"),
101
+ alt.Tooltip("y_pred:Q", format=",.3f", title="Predicted Y"),
117
102
  ],
118
103
  )
119
104
  )
120
105
 
121
106
  area_pred = (
122
- alt.Chart(df_train)
107
+ alt.Chart(df)
108
+ .transform_filter(~alt.datum["test"]) # filter to training data
123
109
  .mark_area(color="#e0f2ff", opacity=0.8)
124
110
  .encode(
125
- x=alt.X("X_sampled:Q", scale=alt.Scale(nice=False), title="x"),
111
+ x=alt.X("X:Q", scale=alt.Scale(nice=False), title="x"),
126
112
  y=alt.Y("y_pred_low:Q", title=""),
127
113
  y2=alt.Y2("y_pred_upp:Q", title=None),
128
114
  tooltip=[
129
- alt.Tooltip("X_sampled:Q", format=",.3f", title="X"),
130
- alt.Tooltip("y_sampled:Q", format=",.3f", title="Y"),
131
- alt.Tooltip("y_pred_med:Q", format=",.3f", title="Predicted Y"),
115
+ alt.Tooltip("X:Q", format=",.3f", title="X"),
116
+ alt.Tooltip("y:Q", format=",.3f", title="Y"),
117
+ alt.Tooltip("y_pred:Q", format=",.3f", title="Predicted Y"),
132
118
  alt.Tooltip("y_pred_low:Q", format=",.3f", title="Predicted Lower Y"),
133
119
  alt.Tooltip("y_pred_upp:Q", format=",.3f", title="Predicted Upper Y"),
134
120
  ],
@@ -153,5 +139,5 @@ def plot_fit_and_intervals(df_train, df_test):
153
139
  return chart
154
140
 
155
141
 
156
- chart = plot_fit_and_intervals(df_train, df_test)
142
+ chart = plot_fit_and_intervals(df)
157
143
  chart