crc-framework 0.2.0__tar.gz → 0.2.1__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.
- {crc_framework-0.2.0 → crc_framework-0.2.1}/Cargo.lock +2 -2
- {crc_framework-0.2.0 → crc_framework-0.2.1}/Cargo.toml +1 -1
- {crc_framework-0.2.0 → crc_framework-0.2.1}/PKG-INFO +5 -3
- {crc_framework-0.2.0 → crc_framework-0.2.1}/README.md +4 -2
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/Cargo.toml +1 -1
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/distribution.rs +53 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/lib.rs +2 -2
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/__init__.py +9 -7
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/_core.pyi +1 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/distributions/__init__.py +5 -3
- crc_framework-0.2.1/python/crc_framework/distributions/point_mass.py +55 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/src/lib.rs +28 -11
- {crc_framework-0.2.0 → crc_framework-0.2.1}/tests/test_sdk.py +19 -11
- {crc_framework-0.2.0 → crc_framework-0.2.1}/.github/workflows/release.yaml +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/.gitignore +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/LICENSE +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/THIRD_PARTY_NOTICES.md +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/constants.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/error.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/metrics.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/ag_params.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/energy_params.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/flood_params.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/mod.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/precipitation_thresholds.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/spei_params.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/wind_params.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/curve_fitting.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/mod.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/spatial/continent_r4.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/spatial/geography_r5.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/spatial/ipcc.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/spatial/mod.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/risk.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/spatial.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/transform.rs +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/pyproject.toml +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/constants.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/distributions/base.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/distributions/empirical.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/distributions/fitted.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/distributions/fitting.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/distributions/hurdle.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/distributions/tabulated.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/metrics/__init__.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/metrics/distribution.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/metrics/microscores.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/metrics/risk.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/metrics/spanning.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/models.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/py.typed +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/spatial.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/transforms/__init__.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/transforms/base.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/transforms/builtin.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/python/crc_framework/transforms/climate.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/tests/test_impacts.py +0 -0
- {crc_framework-0.2.0 → crc_framework-0.2.1}/tests/typing_smoke.py +0 -0
|
@@ -49,7 +49,7 @@ dependencies = [
|
|
|
49
49
|
|
|
50
50
|
[[package]]
|
|
51
51
|
name = "crc-framework-core"
|
|
52
|
-
version = "0.2.
|
|
52
|
+
version = "0.2.1"
|
|
53
53
|
dependencies = [
|
|
54
54
|
"h3o",
|
|
55
55
|
"phf",
|
|
@@ -58,7 +58,7 @@ dependencies = [
|
|
|
58
58
|
|
|
59
59
|
[[package]]
|
|
60
60
|
name = "crc-framework-rs"
|
|
61
|
-
version = "0.2.
|
|
61
|
+
version = "0.2.1"
|
|
62
62
|
dependencies = [
|
|
63
63
|
"crc-framework-core",
|
|
64
64
|
"h3o",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crc-framework
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
6
6
|
Classifier: Operating System :: OS Independent
|
|
@@ -92,6 +92,7 @@ Choose a distribution according to the form of the available data:
|
|
|
92
92
|
- `FittedDistribution` for a known parametric family and its parameters.
|
|
93
93
|
- `HurdleDistribution` for a point mass followed by a truncated parametric
|
|
94
94
|
tail.
|
|
95
|
+
- `PointMassDistribution` for a value that is constant at every probability.
|
|
95
96
|
|
|
96
97
|
`TabulatedDistribution` does not extrapolate by default. Querying outside its
|
|
97
98
|
declared probability range raises an error unless `extrapolate=True` is set.
|
|
@@ -307,8 +308,9 @@ continent and intersecting ISO3 country codes.
|
|
|
307
308
|
The package exports the following primary interfaces:
|
|
308
309
|
|
|
309
310
|
- Distributions: `EmpiricalDistribution`, `TabulatedDistribution`,
|
|
310
|
-
`FittedDistribution`, `HurdleDistribution`, `
|
|
311
|
-
`fit_quantiles`, `fit_hurdle_quantiles`, `fit_all`, and
|
|
311
|
+
`FittedDistribution`, `HurdleDistribution`, `PointMassDistribution`,
|
|
312
|
+
`fit_distribution`, `fit_quantiles`, `fit_hurdle_quantiles`, `fit_all`, and
|
|
313
|
+
`quality_metrics`.
|
|
312
314
|
- Transforms: `impacts`, `ImpactRegistry`, `ImpactFunction`, `ClimateImpact`,
|
|
313
315
|
`LinearImpact`, `SigmoidImpact`, `PiecewiseLinearImpact`, `CallableImpact`,
|
|
314
316
|
and `CallableTransform`.
|
|
@@ -69,6 +69,7 @@ Choose a distribution according to the form of the available data:
|
|
|
69
69
|
- `FittedDistribution` for a known parametric family and its parameters.
|
|
70
70
|
- `HurdleDistribution` for a point mass followed by a truncated parametric
|
|
71
71
|
tail.
|
|
72
|
+
- `PointMassDistribution` for a value that is constant at every probability.
|
|
72
73
|
|
|
73
74
|
`TabulatedDistribution` does not extrapolate by default. Querying outside its
|
|
74
75
|
declared probability range raises an error unless `extrapolate=True` is set.
|
|
@@ -284,8 +285,9 @@ continent and intersecting ISO3 country codes.
|
|
|
284
285
|
The package exports the following primary interfaces:
|
|
285
286
|
|
|
286
287
|
- Distributions: `EmpiricalDistribution`, `TabulatedDistribution`,
|
|
287
|
-
`FittedDistribution`, `HurdleDistribution`, `
|
|
288
|
-
`fit_quantiles`, `fit_hurdle_quantiles`, `fit_all`, and
|
|
288
|
+
`FittedDistribution`, `HurdleDistribution`, `PointMassDistribution`,
|
|
289
|
+
`fit_distribution`, `fit_quantiles`, `fit_hurdle_quantiles`, `fit_all`, and
|
|
290
|
+
`quality_metrics`.
|
|
289
291
|
- Transforms: `impacts`, `ImpactRegistry`, `ImpactFunction`, `ClimateImpact`,
|
|
290
292
|
`LinearImpact`, `SigmoidImpact`, `PiecewiseLinearImpact`, `CallableImpact`,
|
|
291
293
|
and `CallableTransform`.
|
|
@@ -256,6 +256,47 @@ impl Distribution for EmpiricalDistribution {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
/// A degenerate distribution whose entire probability mass is at one value.
|
|
260
|
+
#[derive(Debug, Clone, Copy)]
|
|
261
|
+
pub struct PointMassDistribution {
|
|
262
|
+
location: f64,
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
impl PointMassDistribution {
|
|
266
|
+
pub fn new(location: f64) -> Result<Self> {
|
|
267
|
+
if !location.is_finite() {
|
|
268
|
+
return Err(CrcError::InvalidInput(
|
|
269
|
+
"point-mass location must be finite".into(),
|
|
270
|
+
));
|
|
271
|
+
}
|
|
272
|
+
Ok(Self { location })
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pub fn location(&self) -> f64 {
|
|
276
|
+
self.location
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
impl Distribution for PointMassDistribution {
|
|
281
|
+
fn pdf(&self, _x: f64) -> f64 {
|
|
282
|
+
// A Dirac mass has no finite density with respect to Lebesgue measure.
|
|
283
|
+
0.0
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
fn cdf(&self, x: f64) -> f64 {
|
|
287
|
+
if x < self.location { 0.0 } else { 1.0 }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
fn ppf(&self, probability: f64) -> Result<f64> {
|
|
291
|
+
validate_probability(probability)?;
|
|
292
|
+
Ok(self.location)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
fn sample(&self, size: usize, _seed: Option<u64>) -> Result<Vec<f64>> {
|
|
296
|
+
Ok(vec![self.location; size])
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
259
300
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
260
301
|
pub enum DistributionFamily {
|
|
261
302
|
GenExtreme,
|
|
@@ -1129,6 +1170,18 @@ fn erf(x: f64) -> f64 {
|
|
|
1129
1170
|
mod tests {
|
|
1130
1171
|
use super::*;
|
|
1131
1172
|
|
|
1173
|
+
#[test]
|
|
1174
|
+
fn point_mass_is_exact_at_every_probability() {
|
|
1175
|
+
let distribution = PointMassDistribution::new(2.5).unwrap();
|
|
1176
|
+
assert_eq!(
|
|
1177
|
+
distribution.quantiles(&[0.0, 0.5, 1.0]).unwrap(),
|
|
1178
|
+
vec![2.5; 3]
|
|
1179
|
+
);
|
|
1180
|
+
assert_eq!(distribution.cdf(2.49), 0.0);
|
|
1181
|
+
assert_eq!(distribution.cdf(2.5), 1.0);
|
|
1182
|
+
assert_eq!(distribution.sample(3, Some(42)).unwrap(), vec![2.5; 3]);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1132
1185
|
#[test]
|
|
1133
1186
|
fn return_periods_convert_to_upper_tail_probabilities() {
|
|
1134
1187
|
let distribution = TabulatedDistribution::from_return_periods(
|
|
@@ -10,8 +10,8 @@ pub mod transform;
|
|
|
10
10
|
pub use distribution::{
|
|
11
11
|
DiagnosticMetrics, Distribution, DistributionFamily, EmpiricalDistribution, FitResult,
|
|
12
12
|
FittedDistribution, HurdleDistribution, HurdleQuantileFitDiagnostics, HurdleQuantileFitResult,
|
|
13
|
-
Interpolation, QuantileFitDiagnostics, QuantileFitResult,
|
|
14
|
-
fit_hurdle_quantiles, fit_quantiles,
|
|
13
|
+
Interpolation, PointMassDistribution, QuantileFitDiagnostics, QuantileFitResult,
|
|
14
|
+
TabulatedDistribution, Tail, fit_hurdle_quantiles, fit_quantiles,
|
|
15
15
|
};
|
|
16
16
|
pub use error::{CrcError, Result};
|
|
17
17
|
pub use metrics::{
|
|
@@ -11,6 +11,7 @@ from .distributions import (
|
|
|
11
11
|
HurdleDistribution,
|
|
12
12
|
HurdleQuantileFitDiagnostics,
|
|
13
13
|
HurdleQuantileFitResult,
|
|
14
|
+
PointMassDistribution,
|
|
14
15
|
QuantileFitDiagnostics,
|
|
15
16
|
QuantileFitResult,
|
|
16
17
|
TabulatedDistribution,
|
|
@@ -44,34 +45,35 @@ from .transforms import (
|
|
|
44
45
|
)
|
|
45
46
|
|
|
46
47
|
__all__ = [
|
|
48
|
+
"HORIZONS",
|
|
49
|
+
"PATHWAYS",
|
|
50
|
+
"RISK_FACTORS",
|
|
47
51
|
"BinaryOutcome",
|
|
48
52
|
"CallableImpact",
|
|
49
53
|
"CallableTransform",
|
|
50
54
|
"ClimateImpact",
|
|
51
55
|
"Distribution",
|
|
52
56
|
"EmpiricalDistribution",
|
|
53
|
-
"FitDiagnostics",
|
|
54
57
|
"FitConstraints",
|
|
58
|
+
"FitDiagnostics",
|
|
55
59
|
"FitResult",
|
|
56
60
|
"FittedDistribution",
|
|
61
|
+
"Geography",
|
|
57
62
|
"HurdleDistribution",
|
|
58
63
|
"HurdleQuantileFitDiagnostics",
|
|
59
64
|
"HurdleQuantileFitResult",
|
|
60
|
-
"Geography",
|
|
61
|
-
"HORIZONS",
|
|
62
65
|
"ImpactFunction",
|
|
63
66
|
"ImpactRegistry",
|
|
64
67
|
"LinearImpact",
|
|
65
68
|
"Microscore",
|
|
66
69
|
"MicroscoreSuite",
|
|
67
|
-
"PATHWAYS",
|
|
68
70
|
"Pathway",
|
|
69
71
|
"PiecewiseLinearImpact",
|
|
70
|
-
"
|
|
71
|
-
"RiskFactor",
|
|
72
|
-
"RiskResult",
|
|
72
|
+
"PointMassDistribution",
|
|
73
73
|
"QuantileFitDiagnostics",
|
|
74
74
|
"QuantileFitResult",
|
|
75
|
+
"RiskFactor",
|
|
76
|
+
"RiskResult",
|
|
75
77
|
"ScenarioMetadata",
|
|
76
78
|
"SigmoidImpact",
|
|
77
79
|
"TabulatedDistribution",
|
|
@@ -100,6 +100,7 @@ class NativeRiskResult:
|
|
|
100
100
|
branch_count: int
|
|
101
101
|
|
|
102
102
|
def empirical_distribution(samples: Sequence[float]) -> NativeDistribution: ...
|
|
103
|
+
def point_mass_distribution(location: float) -> NativeDistribution: ...
|
|
103
104
|
def tabulated_distribution(
|
|
104
105
|
probabilities: Sequence[float],
|
|
105
106
|
values: Sequence[float],
|
|
@@ -16,24 +16,26 @@ from .fitting import (
|
|
|
16
16
|
quality_metrics,
|
|
17
17
|
)
|
|
18
18
|
from .hurdle import HurdleDistribution
|
|
19
|
+
from .point_mass import PointMassDistribution
|
|
19
20
|
from .tabulated import Interpolation, TabulatedDistribution, Tail
|
|
20
21
|
|
|
21
22
|
__all__ = [
|
|
22
23
|
"Distribution",
|
|
23
24
|
"DistributionFamily",
|
|
24
25
|
"EmpiricalDistribution",
|
|
25
|
-
"FitDiagnostics",
|
|
26
26
|
"FitConstraints",
|
|
27
|
+
"FitDiagnostics",
|
|
27
28
|
"FitResult",
|
|
28
29
|
"FittedDistribution",
|
|
29
30
|
"HurdleDistribution",
|
|
30
31
|
"HurdleQuantileFitDiagnostics",
|
|
31
32
|
"HurdleQuantileFitResult",
|
|
32
33
|
"Interpolation",
|
|
33
|
-
"
|
|
34
|
-
"Tail",
|
|
34
|
+
"PointMassDistribution",
|
|
35
35
|
"QuantileFitDiagnostics",
|
|
36
36
|
"QuantileFitResult",
|
|
37
|
+
"TabulatedDistribution",
|
|
38
|
+
"Tail",
|
|
37
39
|
"fit_all",
|
|
38
40
|
"fit_distribution",
|
|
39
41
|
"fit_hurdle_quantiles",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
|
|
6
|
+
from crc_framework import _core
|
|
7
|
+
|
|
8
|
+
from .base import ArrayLike
|
|
9
|
+
from .empirical import _evaluate
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PointMassDistribution:
|
|
13
|
+
"""A distribution concentrated exactly at one finite value."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
location: float,
|
|
18
|
+
*,
|
|
19
|
+
_native: _core.NativeDistribution | None = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
self.location = float(location)
|
|
22
|
+
self._native = _native or _core.point_mass_distribution(self.location)
|
|
23
|
+
|
|
24
|
+
def point_mass(self, x: ArrayLike) -> float | npt.NDArray[np.float64]:
|
|
25
|
+
values = np.asarray(x, dtype=np.float64)
|
|
26
|
+
masses = np.where(values == self.location, 1.0, 0.0)
|
|
27
|
+
if values.ndim == 0:
|
|
28
|
+
return float(masses)
|
|
29
|
+
return np.asarray(masses, dtype=np.float64)
|
|
30
|
+
|
|
31
|
+
def pdf(self, x: ArrayLike) -> float | npt.NDArray[np.float64]:
|
|
32
|
+
return _evaluate(self._native, "pdf", x)
|
|
33
|
+
|
|
34
|
+
def cdf(self, x: ArrayLike) -> float | npt.NDArray[np.float64]:
|
|
35
|
+
return _evaluate(self._native, "cdf", x)
|
|
36
|
+
|
|
37
|
+
def ppf(self, probability: ArrayLike) -> float | npt.NDArray[np.float64]:
|
|
38
|
+
return _evaluate(self._native, "ppf", probability)
|
|
39
|
+
|
|
40
|
+
def quantiles(self, probabilities: ArrayLike) -> npt.NDArray[np.float64]:
|
|
41
|
+
values = np.asarray(probabilities, dtype=np.float64)
|
|
42
|
+
return np.asarray(
|
|
43
|
+
self._native.quantiles(values.reshape(-1).tolist()), dtype=np.float64
|
|
44
|
+
).reshape(values.shape)
|
|
45
|
+
|
|
46
|
+
def sample(
|
|
47
|
+
self,
|
|
48
|
+
size: int,
|
|
49
|
+
*,
|
|
50
|
+
rng: np.random.Generator | None = None,
|
|
51
|
+
seed: int | None = None,
|
|
52
|
+
) -> npt.NDArray[np.float64]:
|
|
53
|
+
if rng is not None and seed is not None:
|
|
54
|
+
raise ValueError("provide either rng or seed, not both")
|
|
55
|
+
return np.full(size, self.location, dtype=np.float64)
|
|
@@ -2,8 +2,8 @@ use std::{collections::HashMap, sync::Arc};
|
|
|
2
2
|
|
|
3
3
|
use crc_framework_core::{
|
|
4
4
|
BinaryOutcome, Distribution, DistributionFamily, EmpiricalDistribution, FittedDistribution,
|
|
5
|
-
HurdleDistribution, ImpactContext, ImpactRegistry, Interpolation,
|
|
6
|
-
TabulatedDistribution, Tail, Transform, compute_risk, compute_spanning_set,
|
|
5
|
+
HurdleDistribution, ImpactContext, ImpactRegistry, Interpolation, PointMassDistribution,
|
|
6
|
+
ScenarioMetadata, TabulatedDistribution, Tail, Transform, compute_risk, compute_spanning_set,
|
|
7
7
|
fit_hurdle_quantiles as core_fit_hurdle_quantiles, fit_quantiles as core_fit_quantiles,
|
|
8
8
|
generate_microscores,
|
|
9
9
|
};
|
|
@@ -246,6 +246,18 @@ fn empirical_distribution(samples: Vec<f64>) -> PyResult<PyDistribution> {
|
|
|
246
246
|
})
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
#[pyfunction]
|
|
250
|
+
fn point_mass_distribution(location: f64) -> PyResult<PyDistribution> {
|
|
251
|
+
let distribution = PointMassDistribution::new(location).map_err(py_error)?;
|
|
252
|
+
Ok(PyDistribution {
|
|
253
|
+
inner: Arc::new(distribution),
|
|
254
|
+
family: None,
|
|
255
|
+
shape: None,
|
|
256
|
+
location: Some(location),
|
|
257
|
+
scale: None,
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
249
261
|
fn interpolation(value: &str) -> PyResult<Interpolation> {
|
|
250
262
|
match value {
|
|
251
263
|
"linear_probability" => Ok(Interpolation::LinearProbability),
|
|
@@ -365,6 +377,7 @@ fn fit_candidates(samples: Vec<f64>) -> PyResult<Vec<PyFitResult>> {
|
|
|
365
377
|
#[pyfunction]
|
|
366
378
|
#[pyo3(signature = (probabilities, values, family, weights=None))]
|
|
367
379
|
fn fit_quantiles(
|
|
380
|
+
py: Python<'_>,
|
|
368
381
|
probabilities: Vec<f64>,
|
|
369
382
|
values: Vec<f64>,
|
|
370
383
|
family: &str,
|
|
@@ -372,7 +385,7 @@ fn fit_quantiles(
|
|
|
372
385
|
) -> PyResult<PyQuantileFitResult> {
|
|
373
386
|
let family = DistributionFamily::from_name(family)
|
|
374
387
|
.ok_or_else(|| PyValueError::new_err(format!("unknown distribution family {family}")))?;
|
|
375
|
-
core_fit_quantiles(&probabilities, &values, weights.as_deref(), family)
|
|
388
|
+
py.detach(move || core_fit_quantiles(&probabilities, &values, weights.as_deref(), family))
|
|
376
389
|
.map(quantile_fit_result_py)
|
|
377
390
|
.map_err(py_error)
|
|
378
391
|
}
|
|
@@ -387,6 +400,7 @@ fn fit_quantiles(
|
|
|
387
400
|
weights=None
|
|
388
401
|
))]
|
|
389
402
|
fn fit_hurdle_quantiles(
|
|
403
|
+
py: Python<'_>,
|
|
390
404
|
probabilities: Vec<f64>,
|
|
391
405
|
values: Vec<f64>,
|
|
392
406
|
family: &str,
|
|
@@ -396,14 +410,16 @@ fn fit_hurdle_quantiles(
|
|
|
396
410
|
) -> PyResult<PyHurdleQuantileFitResult> {
|
|
397
411
|
let family = DistributionFamily::from_name(family)
|
|
398
412
|
.ok_or_else(|| PyValueError::new_err(format!("unknown distribution family {family}")))?;
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
413
|
+
py.detach(move || {
|
|
414
|
+
core_fit_hurdle_quantiles(
|
|
415
|
+
&probabilities,
|
|
416
|
+
&values,
|
|
417
|
+
weights.as_deref(),
|
|
418
|
+
family,
|
|
419
|
+
atom_location,
|
|
420
|
+
atom_probability,
|
|
421
|
+
)
|
|
422
|
+
})
|
|
407
423
|
.map(hurdle_quantile_fit_result_py)
|
|
408
424
|
.map_err(py_error)
|
|
409
425
|
}
|
|
@@ -869,6 +885,7 @@ fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
|
869
885
|
m.add_class::<PyRiskLevel>()?;
|
|
870
886
|
m.add_class::<PyRiskResult>()?;
|
|
871
887
|
m.add_function(wrap_pyfunction!(empirical_distribution, m)?)?;
|
|
888
|
+
m.add_function(wrap_pyfunction!(point_mass_distribution, m)?)?;
|
|
872
889
|
m.add_function(wrap_pyfunction!(tabulated_distribution, m)?)?;
|
|
873
890
|
m.add_function(wrap_pyfunction!(return_period_distribution, m)?)?;
|
|
874
891
|
m.add_function(wrap_pyfunction!(fitted_distribution, m)?)?;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
|
-
|
|
5
4
|
from crc_framework import (
|
|
6
5
|
BinaryOutcome,
|
|
7
6
|
EmpiricalDistribution,
|
|
8
7
|
FitConstraints,
|
|
9
8
|
FittedDistribution,
|
|
10
9
|
HurdleDistribution,
|
|
10
|
+
PointMassDistribution,
|
|
11
11
|
RiskFactor,
|
|
12
12
|
ScenarioMetadata,
|
|
13
13
|
TabulatedDistribution,
|
|
@@ -27,6 +27,18 @@ from crc_framework import (
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class DistributionTests(unittest.TestCase):
|
|
30
|
+
def test_point_mass_is_exact_at_every_probability(self):
|
|
31
|
+
distribution = PointMassDistribution(0.0)
|
|
32
|
+
|
|
33
|
+
np.testing.assert_array_equal(
|
|
34
|
+
distribution.quantiles([0.0, 0.5, 0.999, 1.0]),
|
|
35
|
+
np.zeros(4),
|
|
36
|
+
)
|
|
37
|
+
self.assertEqual(distribution.cdf(-0.1), 0.0)
|
|
38
|
+
self.assertEqual(distribution.cdf(0.0), 1.0)
|
|
39
|
+
self.assertEqual(distribution.point_mass(0.0), 1.0)
|
|
40
|
+
np.testing.assert_array_equal(distribution.sample(3), np.zeros(3))
|
|
41
|
+
|
|
30
42
|
def test_return_period_input_uses_canonical_probability(self):
|
|
31
43
|
distribution = TabulatedDistribution.from_return_periods(
|
|
32
44
|
[10, 100, 1000],
|
|
@@ -44,7 +56,9 @@ class DistributionTests(unittest.TestCase):
|
|
|
44
56
|
self.assertEqual(result.distribution.quantiles([0.1, 0.5, 0.9]).shape, (3,))
|
|
45
57
|
self.assertEqual(result.distribution.sample(4, seed=42).shape, (4,))
|
|
46
58
|
diagnostics = quality_metrics(source, result.distribution)
|
|
47
|
-
self.assertAlmostEqual(
|
|
59
|
+
self.assertAlmostEqual(
|
|
60
|
+
diagnostics.ks_statistic, result.diagnostics.ks_statistic
|
|
61
|
+
)
|
|
48
62
|
constrained = fit_distribution(
|
|
49
63
|
source,
|
|
50
64
|
constraints=FitConstraints(probability=0.99, maximum_value=100.0),
|
|
@@ -72,16 +86,12 @@ class DistributionTests(unittest.TestCase):
|
|
|
72
86
|
)
|
|
73
87
|
|
|
74
88
|
def test_sample_fit_rejects_probability_knots(self):
|
|
75
|
-
tabulated = TabulatedDistribution(
|
|
76
|
-
[0.1, 0.3, 0.6, 0.9], [0.0, 1.0, 2.0, 3.0]
|
|
77
|
-
)
|
|
89
|
+
tabulated = TabulatedDistribution([0.1, 0.3, 0.6, 0.9], [0.0, 1.0, 2.0, 3.0])
|
|
78
90
|
with self.assertRaisesRegex(ValueError, "fit_quantiles"):
|
|
79
91
|
fit_distribution(tabulated) # type: ignore[arg-type]
|
|
80
92
|
|
|
81
93
|
def test_hurdle_constructor_preserves_atom_and_truncated_tail(self):
|
|
82
|
-
base = FittedDistribution.from_parameters(
|
|
83
|
-
"gumbel_r", location=0.5, scale=1.2
|
|
84
|
-
)
|
|
94
|
+
base = FittedDistribution.from_parameters("gumbel_r", location=0.5, scale=1.2)
|
|
85
95
|
hurdle = HurdleDistribution(base, atom_probability=0.4)
|
|
86
96
|
self.assertEqual(hurdle.ppf(0.4), 0.0)
|
|
87
97
|
self.assertEqual(hurdle.point_mass(0.0), 0.4)
|
|
@@ -158,9 +168,7 @@ class PipelineTests(unittest.TestCase):
|
|
|
158
168
|
class SpatialAndConstantTests(unittest.TestCase):
|
|
159
169
|
def test_reference_spatial_tables_are_exposed_without_silent_defaults(self):
|
|
160
170
|
self.assertEqual(lookup_continent(594475228122316799), "Europe")
|
|
161
|
-
self.assertEqual(
|
|
162
|
-
lookup_ipcc_region(594479566039285759), "Greenland/Iceland"
|
|
163
|
-
)
|
|
171
|
+
self.assertEqual(lookup_ipcc_region(594479566039285759), "Greenland/Iceland")
|
|
164
172
|
geography = lookup_geography(600550049193132031)
|
|
165
173
|
self.assertIsNotNone(geography)
|
|
166
174
|
self.assertEqual(geography.continent, "Africa")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/energy_params.rs
RENAMED
|
File without changes
|
{crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/flood_params.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/spei_params.rs
RENAMED
|
File without changes
|
{crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/climate/wind_params.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/spatial/continent_r4.rs
RENAMED
|
File without changes
|
{crc_framework-0.2.0 → crc_framework-0.2.1}/crates/core/src/reference/spatial/geography_r5.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|