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.
- {quantile-forest-1.3.2/quantile_forest.egg-info → quantile-forest-1.3.4}/PKG-INFO +2 -2
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/README.md +1 -1
- quantile-forest-1.3.4/docs/_static/favicon.ico +0 -0
- quantile-forest-1.3.4/docs/_static/quantile-forest-logo-white.png +0 -0
- quantile-forest-1.3.4/docs/_static/quantile-forest-logo.png +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/conf.py +17 -3
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/index.rst +5 -5
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/install.rst +1 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinxext/gallery.py +8 -16
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/user_guide.rst +16 -9
- {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_conformalized.py +24 -22
- {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_example.py +43 -57
- {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_extrapolation.py +23 -44
- {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_interpolation.py +48 -59
- {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_intervals.py +15 -29
- {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/plot_quantile_multioutput.py +20 -16
- quantile-forest-1.3.4/examples/plot_quantile_vs_standard.py +94 -0
- quantile-forest-1.3.4/examples/plot_quantile_weighting.py +177 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/_quantile_forest.py +26 -25
- quantile-forest-1.3.4/quantile_forest/_quantile_forest_fast.pxd +47 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/_quantile_forest_fast.pyx +78 -80
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/tests/test_quantile_forest.py +32 -10
- quantile-forest-1.3.4/quantile_forest/version.py +1 -0
- quantile-forest-1.3.4/quantile_forest/version.txt +1 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4/quantile_forest.egg-info}/PKG-INFO +2 -2
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/SOURCES.txt +13 -10
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/top_level.txt +1 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/setup.py +12 -1
- quantile-forest-1.3.2/quantile_forest/_quantile_forest_fast.pxd +0 -48
- quantile-forest-1.3.2/quantile_forest/tests/examples/plot_quantile_vs_standard.py +0 -93
- quantile-forest-1.3.2/quantile_forest/tests/examples/plot_quantile_weighting.py +0 -152
- quantile-forest-1.3.2/quantile_forest/version.py +0 -1
- quantile-forest-1.3.2/quantile_forest/version.txt +0 -1
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/LICENSE +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/MANIFEST.in +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/Makefile +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_static/css/gallery.css +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_static/css/theme_overrides.css +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_static/js/copybutton.js +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_templates/class.rst +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_templates/navbar-project.html +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/_templates/sidebar-logo.html +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/api.rst +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/make.bat +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/references.rst +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/refs.bib +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinx_requirements.txt +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinxext/__init__.py +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/docs/sphinxext/utils.py +1 -1
- {quantile-forest-1.3.2/quantile_forest/tests → quantile-forest-1.3.4}/examples/__init__.py +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/pyproject.toml +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/__init__.py +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/_min_dependencies.py +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest/tests/__init__.py +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/dependency_links.txt +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/not-zip-safe +0 -0
- {quantile-forest-1.3.2 → quantile-forest-1.3.4}/quantile_forest.egg-info/requires.txt +0 -0
- {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.
|
|
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
|
[](https://pypi.org/project/quantile-forest)
|
|
37
37
|
[](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
|
[](https://pypi.org/project/quantile-forest)
|
|
4
4
|
[](https://github.com/zillow/quantile-forest/blob/main/LICENSE)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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[
|
|
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
|
-
>>>
|
|
122
|
-
>>>
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
135
|
-
|
|
135
|
+
# Calculate coverage and width metrics.
|
|
136
|
+
metrics = (
|
|
136
137
|
df.groupby("strategy")
|
|
137
138
|
.apply(
|
|
138
|
-
lambda
|
|
139
|
+
lambda grp: pd.Series(
|
|
139
140
|
{
|
|
140
|
-
"coverage": coverage_score(
|
|
141
|
-
"width": mean_width_score(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
+
y_pred_func = qrf.predict(X_func, quantiles=quantiles)
|
|
45
|
+
y_pred_test = qrf.predict(X_test, quantiles=quantiles)
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
df = pd.DataFrame(
|
|
47
48
|
{
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"y_pred_upp":
|
|
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(
|
|
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("
|
|
83
|
-
y=alt.Y("
|
|
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("
|
|
87
|
-
alt.Tooltip("
|
|
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(
|
|
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("
|
|
97
|
-
y=alt.Y("
|
|
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("
|
|
101
|
-
alt.Tooltip("
|
|
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(
|
|
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("
|
|
111
|
-
y=alt.Y("
|
|
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("
|
|
115
|
-
alt.Tooltip("
|
|
116
|
-
alt.Tooltip("
|
|
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(
|
|
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("
|
|
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("
|
|
130
|
-
alt.Tooltip("
|
|
131
|
-
alt.Tooltip("
|
|
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(
|
|
142
|
+
chart = plot_fit_and_intervals(df)
|
|
157
143
|
chart
|