Просмотр исходного кода

Complete theta naming migration in reporting

Martin Horvat 2 дней назад
Родитель
Сommit
9882b5527a
2 измененных файлов с 123 добавлено и 23 удалено
  1. 27 23
      src/irae_risk/reporting.py
  2. 96 0
      tests/test_reporting.py

+ 27 - 23
src/irae_risk/reporting.py

@@ -98,10 +98,10 @@ def build_plot_data_basic(df_data, df_fit_index, lg, n_grid=100, verbose=False):
             print(f"Calculating: {scale}, {dataset}")
             print(f"Calculating: {scale}, {dataset}")
 
 
         x = g["X"].to_numpy()
         x = g["X"].to_numpy()
-        pars = df_fit_index.loc[(scale, dataset), "pars"]
+        theta = df_fit_index.loc[(scale, dataset), "theta"]
 
 
         x_fit = np.linspace(x.min(), x.max(), n_grid)
         x_fit = np.linspace(x.min(), x.max(), n_grid)
-        y_fit = lg.model(x_fit, pars)
+        y_fit = lg.model(x_fit, theta)
 
 
         points.append(_make_points_df(g, scale, dataset))
         points.append(_make_points_df(g, scale, dataset))
         fits.append(_make_fit_df(x_fit, y_fit, scale, dataset))
         fits.append(_make_fit_df(x_fit, y_fit, scale, dataset))
@@ -133,20 +133,20 @@ def build_cost_perturbation_data(
         g = df_data[(df_data["scale"] == scale) & (df_data["dataset"] == dataset)]
         g = df_data[(df_data["scale"] == scale) & (df_data["dataset"] == dataset)]
         x, y = g["X"].to_numpy(), g["Y"].to_numpy()
         x, y = g["X"].to_numpy(), g["Y"].to_numpy()
 
 
-        pars = np.array(
-            df_fit.loc[(df_fit["scale"] == scale) & (df_fit["dataset"] == dataset), "pars"].iloc[0],
+        theta = np.array(
+            df_fit.loc[(df_fit["scale"] == scale) & (df_fit["dataset"] == dataset), "theta"].iloc[0],
             copy=True,
             copy=True,
         )
         )
 
 
-        for j in range(len(pars)):
+        for j in range(len(theta)):
             if verbose:
             if verbose:
                 print(f"\tparameter {j}")
                 print(f"\tparameter {j}")
 
 
             cost = np.empty_like(dpar_grid, dtype=float)
             cost = np.empty_like(dpar_grid, dtype=float)
             for i, dp in enumerate(dpar_grid):
             for i, dp in enumerate(dpar_grid):
-                pars_tmp = pars.copy()
-                pars_tmp[j] += dp
-                cost[i] = lg.get_cost(x, y, pars_tmp, jac=False)
+                theta_tmp = theta.copy()
+                theta_tmp[j] += dp
+                cost[i] = lg.get_cost(x, y, theta_tmp, jac=False)
 
 
             rows.append(pd.DataFrame({
             rows.append(pd.DataFrame({
                 "scale": scale,
                 "scale": scale,
@@ -173,18 +173,18 @@ def build_plot_data(df_data, df_fit_index, alpha, lg, n_grid=100, verbose=False)
             print(f"Calculating: {scale}, {dataset}")
             print(f"Calculating: {scale}, {dataset}")
 
 
         x = g["X"].to_numpy()
         x = g["X"].to_numpy()
-        pars = df_fit_index.loc[(scale, dataset), "pars"]
+        theta = df_fit_index.loc[(scale, dataset), "theta"]
         cov = df_fit_index.loc[(scale, dataset), "cov"]
         cov = df_fit_index.loc[(scale, dataset), "cov"]
 
 
         x_fit = np.linspace(x.min(), x.max(), n_grid)
         x_fit = np.linspace(x.min(), x.max(), n_grid)
-        y_fit = lg.model(x_fit, pars)
+        y_fit = lg.model(x_fit, theta)
 
 
         points.append(_make_points_df(g, scale, dataset))
         points.append(_make_points_df(g, scale, dataset))
         fits.append(_make_fit_df(x_fit, y_fit, scale, dataset))
         fits.append(_make_fit_df(x_fit, y_fit, scale, dataset))
 
 
         for method in ("normal", "delta"):
         for method in ("normal", "delta"):
             qfun = getattr(lg, f"get_model_quantiles_{method}")
             qfun = getattr(lg, f"get_model_quantiles_{method}")
-            y_low, y_high = qfun(x_fit, probs, pars, cov)
+            y_low, y_high = qfun(x_fit, probs, theta, cov)
 
 
             cis.append(pd.DataFrame({
             cis.append(pd.DataFrame({
                 "scale": scale,
                 "scale": scale,
@@ -203,11 +203,11 @@ def build_plot_data(df_data, df_fit_index, alpha, lg, n_grid=100, verbose=False)
     )
     )
 
 
 
 
-def load_or_build_bootstrap_pars(
+def load_or_build_bootstrap_theta(
     df_data,
     df_data,
     lg,
     lg,
     results_path,
     results_path,
-    filename="boots_pars_results.pkl",
+    filename="boots_theta_results.pkl",
     n_boots=10000,
     n_boots=10000,
     methods=("normal", "nonparam_boots", "nonparam_stratified_boots", "parametric_boots"),
     methods=("normal", "nonparam_boots", "nonparam_stratified_boots", "parametric_boots"),
     verbose=False,
     verbose=False,
@@ -234,9 +234,9 @@ def load_or_build_bootstrap_pars(
             if verbose:
             if verbose:
                 print(f"\tmethod: {method}")
                 print(f"\tmethod: {method}")
 
 
-            par_fun = getattr(lg, f"get_{method}_pars")
+            theta_fun = getattr(lg, f"get_{method}_theta")
             try:
             try:
-                out[(scale, dataset, method)] = par_fun(x, y, m=n_boots)
+                out[(scale, dataset, method)] = theta_fun(x, y, m=n_boots)
             except Exception:
             except Exception:
                 if verbose:
                 if verbose:
                     print("\tfailed")
                     print("\tfailed")
@@ -253,7 +253,7 @@ def build_plot_data_full_ci(
     df_fit_index,
     df_fit_index,
     lg,
     lg,
     alpha,
     alpha,
-    boots_pars_results,
+    boots_theta_results,
     n_grid=100,
     n_grid=100,
     analytic_methods=("normal", "delta"),
     analytic_methods=("normal", "delta"),
     bootstrap_methods=("nonparam_boots", "parametric_boots"),
     bootstrap_methods=("nonparam_boots", "parametric_boots"),
@@ -272,11 +272,11 @@ def build_plot_data_full_ci(
             print(f"Calculating plot data: {scale}, {dataset}")
             print(f"Calculating plot data: {scale}, {dataset}")
 
 
         x = g["X"].to_numpy()
         x = g["X"].to_numpy()
-        pars = df_fit_index.loc[(scale, dataset), "pars"]
+        theta = df_fit_index.loc[(scale, dataset), "theta"]
         cov = df_fit_index.loc[(scale, dataset), "cov"]
         cov = df_fit_index.loc[(scale, dataset), "cov"]
 
 
         x_fit = np.linspace(x.min(), x.max(), n_grid)
         x_fit = np.linspace(x.min(), x.max(), n_grid)
-        y_fit = lg.model(x_fit, pars)
+        y_fit = lg.model(x_fit, theta)
 
 
         points.append(_make_points_df(g, scale, dataset))
         points.append(_make_points_df(g, scale, dataset))
         fits.append(_make_fit_df(x_fit, y_fit, scale, dataset))
         fits.append(_make_fit_df(x_fit, y_fit, scale, dataset))
@@ -286,7 +286,7 @@ def build_plot_data_full_ci(
                 print(f"\tanalytic CI: {method}")
                 print(f"\tanalytic CI: {method}")
 
 
             qfun = getattr(lg, f"get_model_quantiles_{method}")
             qfun = getattr(lg, f"get_model_quantiles_{method}")
-            y_low, y_high = qfun(x_fit, probs, pars, cov)
+            y_low, y_high = qfun(x_fit, probs, theta, cov)
 
 
             cis.append(pd.DataFrame({
             cis.append(pd.DataFrame({
                 "scale": scale,
                 "scale": scale,
@@ -303,11 +303,15 @@ def build_plot_data_full_ci(
             if verbose:
             if verbose:
                 print(f"\tbootstrap CI: {method}")
                 print(f"\tbootstrap CI: {method}")
 
 
-            bpars = boots_pars_results[(scale, dataset, method)]
-            if bpars is None:
+            bootstrap_theta = boots_theta_results[(scale, dataset, method)]
+            if bootstrap_theta is None:
                 continue
                 continue
 
 
-            quant = np.quantile([lg.model(x_fit, p) for p in bpars], probs, axis=0)
+            quant = np.quantile(
+                [lg.model(x_fit, theta_i) for theta_i in bootstrap_theta],
+                probs,
+                axis=0,
+            )
             y_low, y_high = quant
             y_low, y_high = quant
 
 
             cis.append(pd.DataFrame({
             cis.append(pd.DataFrame({
@@ -642,4 +646,4 @@ def plot_from_dataframes(
 
 
 
 
 # alias, if you want the previous name to remain available
 # alias, if you want the previous name to remain available
-plot_from_dataframes_full_ci = plot_from_dataframes
+plot_from_dataframes_full_ci = plot_from_dataframes

+ 96 - 0
tests/test_reporting.py

@@ -0,0 +1,96 @@
+import numpy as np
+import pandas as pd
+
+from irae_risk import reporting
+from irae_risk.logistic import LogisticPolyRegression
+
+
+def _reporting_frames():
+    df_data = pd.DataFrame(
+        {
+            "X": [-1.0, 0.0, 1.0, 2.0],
+            "Y": [0, 0, 1, 1],
+            "scale": ["plain"] * 4,
+            "dataset": ["FULL"] * 4,
+        }
+    )
+    df_fit = pd.DataFrame(
+        {
+            "scale": ["plain"],
+            "dataset": ["FULL"],
+            "theta": [np.array([-0.2, 0.8])],
+            "cov": [np.eye(2) * 0.01],
+        }
+    )
+    return df_data, df_fit
+
+
+def test_reporting_builders_read_theta_column():
+    df_data, df_fit = _reporting_frames()
+    model = LogisticPolyRegression(degree=1)
+    df_fit_index = df_fit.set_index(["scale", "dataset"])
+
+    points, fits = reporting.build_plot_data_basic(
+        df_data,
+        df_fit_index,
+        model,
+        n_grid=5,
+    )
+    _, _, intervals = reporting.build_plot_data(
+        df_data,
+        df_fit_index,
+        alpha=0.05,
+        lg=model,
+        n_grid=5,
+    )
+
+    assert len(points) == len(df_data)
+    assert len(fits) == 5
+    assert set(intervals["method"]) == {"normal", "delta"}
+
+
+def test_full_ci_builder_accepts_bootstrap_theta_results():
+    df_data, df_fit = _reporting_frames()
+    model = LogisticPolyRegression(degree=1)
+    df_fit_index = df_fit.set_index(["scale", "dataset"])
+    bootstrap_theta = {
+        ("plain", "FULL", "parametric_boots"): np.array(
+            [[-0.3, 0.7], [-0.2, 0.8], [-0.1, 0.9]]
+        )
+    }
+
+    _, _, intervals = reporting.build_plot_data_full_ci(
+        df_data,
+        df_fit_index,
+        model,
+        alpha=0.05,
+        boots_theta_results=bootstrap_theta,
+        n_grid=5,
+        analytic_methods=(),
+        bootstrap_methods=("parametric_boots",),
+    )
+
+    assert set(intervals["method"]) == {"parametric_boots"}
+    assert set(intervals["ci_source"]) == {"bootstrap"}
+
+
+def test_bootstrap_loader_calls_theta_api(tmp_path):
+    df_data, _ = _reporting_frames()
+
+    class ThetaSampler:
+        def get_normal_theta(self, x, y, m):
+            assert len(x) == len(y) == 4
+            return np.full((m, 2), 1.5)
+
+    result = reporting.load_or_build_bootstrap_theta(
+        df_data,
+        ThetaSampler(),
+        tmp_path,
+        n_boots=3,
+        methods=("normal",),
+    )
+
+    samples = result[("plain", "FULL", "normal")]
+    assert samples.shape == (3, 2)
+    assert np.all(samples == 1.5)
+    assert (tmp_path / "boots_theta_results.pkl").is_file()