diff --git a/auton_survival/estimators.py b/auton_survival/estimators.py index 0a5a309..39b4758 100644 --- a/auton_survival/estimators.py +++ b/auton_survival/estimators.py @@ -118,62 +118,6 @@ def _fit_dcm(features, outcomes, random_seed, **hyperparams): return model - # if len(layers): model = DeepCoxMixture(k=k, inputdim=features.shape[1], hidden=layers[0]) - # else: model = CoxMixture(k=k, inputdim=features.shape[1]) - - # x = torch.from_numpy(features.values.astype('float32')) - # t = torch.from_numpy(outcomes['time'].values.astype('float32')) - # e = torch.from_numpy(outcomes['event'].values.astype('float32')) - - # vidx = _get_valid_idx(x.shape[0], 0.15, random_seed) - - # train_data = (x[~vidx], t[~vidx], e[~vidx]) - # val_data = (x[vidx], t[vidx], e[vidx]) - - # (model, breslow_splines, unique_times) = train(model, - # train_data, - # val_data, - # epochs=epochs, - # lr=lr, bs=bs, - # use_posteriors=True, - # patience=5, - # return_losses=False, - # smoothing_factor=smoothing_factor) - - #return (model, breslow_splines, unique_times) - -# THIS IS 1 OF 2 _PREDICT_DCM FUNCTIONS HERE BUT THIS ONE THROWS A BUG SO I USE _PREDICT_DCM FUNCTION BELOW -# def _predict_dcm(model, features, times): - -# """Predict survival probabilities at specified time(s) using the -# Deep Cox Mixtures model. - -# Parameters -# ----------- -# model : Trained instance of the Deep Cox Mixtures model. -# features : pd.DataFrame -# A pandas dataframe with rows corresponding to individual -# samples and columns as covariates. -# times: float or list -# A float or list of the times at which to compute -# the survival probability. - -# Returns -# ----------- -# np.array : An array of the survival probabilites at each -# time point in times. - -# """ - -# #raise NotImplementedError() - -# survival_predictions = model.predict_survival(features, times) -# if len(times)>1: -# survival_predictions = pd.DataFrame(survival_predictions, columns=times).T -# return __interpolate_missing_times(survival_predictions, times) -# else: -# return survival_predictions - def _fit_dcph(features, outcomes, random_seed, **hyperparams): """Fit a Deep Cox Proportional Hazards Model/Farragi Simon Network [1,2] @@ -228,55 +172,6 @@ def _fit_dcph(features, outcomes, random_seed, **hyperparams): return model - #raise NotImplementedError() - # import torch - # import torchtuples as ttup - - # from pycox.models import CoxPH - - # torch.manual_seed(random_seed) - # np.random.seed(random_seed) - - # layers = hyperparams.get('layers', [100]) - # lr = hyperparams.get('lr', 1e-3) - # bs = hyperparams.get('bs', 100) - # epochs = hyperparams.get('epochs', 50) - # activation = hyperparams.get('activation', 'relu') - - # if activation == 'relu': activation = torch.nn.ReLU - # elif activation == 'relu6': activation = torch.nn.ReLU6 - # elif activation == 'tanh': activation = torch.nn.Tanh - # else: raise NotImplementedError("Activation function not implemented") - - # x = features.values.astype('float32') - # t = outcomes['time'].values.astype('float32') - # e = outcomes['event'].values.astype('bool') - - # in_features = x.shape[1] - # out_features = 1 - # batch_norm = False - # dropout = 0.0 - - # net = ttup.practical.MLPVanilla(in_features, layers, - # out_features, batch_norm, dropout, - # activation=activation, - # output_bias=False) - - # model = CoxPH(net, torch.optim.Adam) - - # vidx = _get_valid_idx(x.shape[0], 0.15, random_seed) - - # y_train, y_val = (t[~vidx], e[~vidx]), (t[vidx], e[vidx]) - # val_data = x[vidx], y_val - - # callbacks = [ttup.callbacks.EarlyStopping()] - # model.fit(x[~vidx], y_train, bs, epochs, callbacks, True, - # val_data=val_data, - # val_batch_size=bs) - # model.compute_baseline_hazards() - - # return model - def __interpolate_missing_times(survival_predictions, times): """Interpolate survival probabilities at missing time points. @@ -771,14 +666,14 @@ def __init__(self, treated_model, control_model): def predict_counterfactual_survival(self, features, times): - control_outcomes = self.control_model.predict_survival(features, times) treated_outcomes = self.treated_model.predict_survival(features, times) + control_outcomes = self.control_model.predict_survival(features, times) return treated_outcomes, control_outcomes def predict_counterfactual_risk(self, features, times): - control_outcomes = self.control_model.predict_risk(features, times) treated_outcomes = self.treated_model.predict_risk(features, times) + control_outcomes = self.control_model.predict_risk(features, times) return treated_outcomes, control_outcomes \ No newline at end of file diff --git a/auton_survival/experiments.py b/auton_survival/experiments.py index 50b1cc3..0676fcd 100644 --- a/auton_survival/experiments.py +++ b/auton_survival/experiments.py @@ -1,3 +1,29 @@ +# coding=utf-8 +# MIT License + +# Copyright (c) 2022 Carnegie Mellon University, Auton Lab + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities to perform cross-validation.""" + +from copy import deepcopy import numpy as np from auton_survival.estimators import SurvivalModel, CounterfactualSurvivalModel @@ -94,17 +120,23 @@ def fit(self, features, outcomes, ret_trained_model=True): self.folds = folds - unique_times = np.unique(outcomes['time'].values) - - time_min, time_max = unique_times.min(), unique_times.max() - + unique_times = np.unique(outcomes.time.values) + time_max, time_min = unique_times.max(), unique_times.min() + for fold in range(self.cv_folds): - - fold_outcomes = outcomes.loc[folds==fold, 'time'] - - if fold_outcomes.min() > time_min: time_min = fold_outcomes.min() - if fold_outcomes.max() < time_max: time_max = fold_outcomes.max() + + time_test = outcomes.loc[folds==fold, 'time'] + time_train = outcomes.loc[folds!=fold, 'time'] + if time_test.min() > time_min: + time_min = time_test.min() + + if (time_test.max() < time_max)|(time_train.max() < time_max): + if time_test.max() > time_train.max(): + time_max = max(time_test[time_test < time_train.max()]) + else: + time_max = max(time_test[time_test < time_test.max()]) + unique_times = unique_times[unique_times>=time_min] unique_times = unique_times[unique_times follow-up time + max_follow_up = outcomes_train.time.max() + predictions_test = predictions_test[outcomes_test.time.values < max_follow_up] + outcomes_test = outcomes_test.loc[outcomes_test.time.values < max_follow_up] # Compute IBS - score = survival_regression_metric('ibs', outcomes_train, outcomes_test, - predictions_test, unique_times) + score = survival_regression_metric('ibs', outcomes_train, predictions_test, + unique_times, outcomes_test) score_per_fold.append(score) current_score = np.mean(score_per_fold) @@ -235,6 +273,8 @@ class CounterfactualSurvivalRegressionCV: """ + _VALID_CF_METHODS = ['dsm', 'dcph', 'dcm', 'rsf', 'cph'] + def __init__(self, model, cv_folds=5, random_seed=0, hyperparam_grid={}): self.model = model @@ -277,11 +317,9 @@ def fit(self, features, outcomes, interventions): """ - - treated, control = interventions==1, interventions!=1 - treated_model = self.treated_experiment.fit(features.loc[treated], - outcomes.loc[treated]) - control_model = self.control_experiment.fit(features.loc[control], - outcomes.loc[control]) + treated_model = self.treated_experiment.fit(features.loc[interventions==1], + outcomes.loc[interventions==1]) + control_model = self.control_experiment.fit(features.loc[interventions!=1], + outcomes.loc[interventions!=1]) return CounterfactualSurvivalModel(treated_model, control_model) diff --git a/auton_survival/metrics.py b/auton_survival/metrics.py index a88e828..b22b341 100644 --- a/auton_survival/metrics.py +++ b/auton_survival/metrics.py @@ -24,22 +24,20 @@ """Tools to compute metrics used to assess survival outcomes and survival model performance.""" -from sksurv import metrics, util from lifelines import KaplanMeierFitter, CoxPHFitter - -from sklearn.metrics import auc - import pandas as pd import numpy as np - +from sksurv import metrics, util +from scipy.optimize import fsolve +from sklearn.metrics import auc from tqdm import tqdm - import warnings -def survival_diff_metric(metric, outcomes, treatment_indicator, - weights=None, horizon=None, interpolate=True, - weights_clip=1e-2, n_bootstrap=None, - size_bootstrap=1.0, random_seed=0): +def treatment_effect(metric, outcomes, treatment_indicator, + weights=None, horizons=None, risks=None, + interpolate=True, weights_clip=1e-2, + n_bootstrap=None, size_bootstrap=1.0, + random_seed=0): """Compute metrics for comparing population level survival outcomes across treatment arms. @@ -50,7 +48,7 @@ def survival_diff_metric(metric, outcomes, treatment_indicator, The metric to evalute for comparing survival outcomes. Options include: - `median` - - `time_to` + - `tar` - `hazard_ratio` - `restricted_mean` - `survival_at` @@ -63,10 +61,14 @@ def survival_diff_metric(metric, outcomes, treatment_indicator, weights : pd.Series, default=None Treatment assignment propensity scores, \( \widehat{\mathbb{P}}(A|X=x) \). If `None`, all weights are set to \( 0.5 \). Default is `None`. - horizon : float - The time horizon at which to compare the survival curves. + horizons : float or int or array of floats or ints, default=None + Event horizon(s) at which to compute the metric. Must be specified for metric 'restricted_mean' and 'survival_at'. For 'hazard_ratio' this is ignored. + risks : float or array of floats + The risk level (0-1) at which to compare times between treatment arms. + Must be specified for metric 'tar'. + Ignored for other metrics. interpolate : bool, default=True Whether to interpolate the survival curves. weights_clip : float @@ -88,10 +90,17 @@ def survival_diff_metric(metric, outcomes, treatment_indicator, """ assert metric in ['median', 'hazard_ratio', 'restricted_mean', - 'survival_at', 'time_to'] - - if metric in ['restricted_mean', 'survival_at', 'time_to']: - assert horizon is not None, "Please specify Event Horizon" + 'survival_at', 'tar'] + + if metric in ['restricted_mean', 'survival_at']: + assert horizons is not None, "Please specify Event Horizon" + assert risks is None, "Risks must be non for 'restricted_mean' and \ +'survival_at' metrics" + + if metric in ['tar']: + assert risks is not None, "Please specify risk level(s) at \ +which to compare time-to-event." + assert horizons is None, "Horizons must be none for 'tar' metric." if metric == 'hazard_ratio': warnings.warn("WARNING: You are computing Hazard Ratios.\n Make sure you have tested the PH Assumptions.") @@ -101,6 +110,12 @@ def survival_diff_metric(metric, outcomes, treatment_indicator, # Bootstrapping ... if n_bootstrap is not None: assert isinstance(n_bootstrap, int), '`bootstrap` must be None or int' + + if isinstance(horizons, (int, float)): + horizons = [horizons] + + if isinstance(risks, (int, float)): + risks = [risks] if isinstance(n_bootstrap, int): print('Bootstrapping... ', n_bootstrap, @@ -120,12 +135,12 @@ def survival_diff_metric(metric, outcomes, treatment_indicator, if metric == 'survival_at': _metric = _survival_at_diff - elif metric == 'time_to': - _metric = _time_to_diff + elif metric == 'tar': + _metric = _tar elif metric == 'restricted_mean': _metric = _restricted_mean_diff elif metric == 'median': - _metric = _time_to_diff + _metric = _median # Lifelines .median_survival_time_? elif metric == 'hazard_ratio': _metric = _hazard_ratio else: raise NotImplementedError() @@ -133,22 +148,24 @@ def survival_diff_metric(metric, outcomes, treatment_indicator, if n_bootstrap is None: return _metric(treated_outcomes, control_outcomes, - horizon=horizon, + horizons=horizons, + risks=risks, interpolate=interpolate, treated_weights=iptw_weights[treatment_indicator], control_weights=iptw_weights[~treatment_indicator]) else: return [_metric(treated_outcomes, control_outcomes, - horizon=horizon, + horizons=horizons, + risks=risks, interpolate=interpolate, treated_weights=iptw_weights[treatment_indicator], control_weights=iptw_weights[~treatment_indicator], size_bootstrap=size_bootstrap, random_seed=i) for i in range(n_bootstrap)] -def survival_regression_metric(metric, outcomes_train, outcomes_test, - predictions, times): +def survival_regression_metric(metric, outcomes_train, predictions, + times, outcomes_test=None): """Compute metrics to assess survival model performance. Parameters @@ -178,6 +195,19 @@ def survival_regression_metric(metric, outcomes_train, outcomes_test, """ + if isinstance(times, (float,int)): + times = [times] + + if outcomes_test is None: + outcomes_test = outcomes_train + warnings.warn("You are are evaluating model performance on the \ +same data used to train the model.") + + assert max(times) < outcomes_train.time.max(), "Times should \ +be within the range of training set times to avoid exterpolation." + assert max(times) < outcomes_test.time.max(), "Times \ +must be within the range of test set times." + survival_train = util.Surv.from_dataframe('event', 'time', outcomes_train) survival_test = util.Surv.from_dataframe('event', 'time', outcomes_test) predictions_test = predictions @@ -201,10 +231,10 @@ def survival_regression_metric(metric, outcomes_train, outcomes_test, else: raise NotImplementedError() - + def phenotype_purity(phenotypes_train, outcomes_train, phenotypes_test=None, outcomes_test=None, - strategy='instantaneous', horizon=None, + strategy='instantaneous', horizons=None, bootstrap=None): """Compute the brier score to assess survival model performance for phenotypes. @@ -227,7 +257,7 @@ def phenotype_purity(phenotypes_train, outcomes_train, Options include: - `instantaneous` : Compute the brier score. - `integrated` : Compute the integrated brier score. - horizon : float or int or np.array of floats or ints, default=None + horizons : float or int or an array of floats or ints, default=None Event horizon(s) at which to compute the metric bootstrap : integer, default=None The number of bootstrap iterations. @@ -240,8 +270,6 @@ def phenotype_purity(phenotypes_train, outcomes_train, """ - # CODE UPDATE: enable phenotype purity to be computed for the test set... - # without specifying folds to determine the train/test sets when folds are inapplicable (no CV) np.random.seed(0) if (outcomes_test is None) & (phenotypes_test is not None): @@ -249,17 +277,19 @@ def phenotype_purity(phenotypes_train, outcomes_train, if (outcomes_test is not None) & (phenotypes_test is None): raise Exception("Specify phenotypes for test set.") - assert horizon is not None, "Please specify Event Horizon" + assert horizons is not None, "Please specify Event Horizon" - if isinstance(horizon, float) | isinstance(horizon, int): - horizon = [horizon] + if isinstance(horizons, (float,int)): + horizons = [horizons] if outcomes_test is None: phenotypes_test = phenotypes_train outcomes_test = outcomes_train warnings.warn("You are are estimating survival probabilities for \ - the same dataset used to estimate the censoring \ - distribution.") +the same dataset used to estimate the censoring distribution.") + + assert outcomes_test.time.max() >= outcomes_train.time.max(), "Test \ +set times must be within the range of training set follow-up times." survival_curves = {} for phenotype in np.unique(phenotypes_train): @@ -272,28 +302,28 @@ def phenotype_purity(phenotypes_train, outcomes_train, if strategy == 'instantaneous': - predictions = np.zeros((len(survival_test), len(horizon))) + predictions = np.zeros((len(survival_test), len(horizons))) for phenotype in set(phenotypes_test): - predictions[phenotypes_test==phenotype, :] = survival_curves[phenotype].predict(times=horizon, + predictions[phenotypes_test==phenotype, :] = survival_curves[phenotype].predict(times=horizons, interpolate=True) if bootstrap is None: return metrics.brier_score(survival_train, survival_test, - predictions, horizon)[1] + predictions, horizons)[1] else: scores = [] for i in tqdm(range(bootstrap)): idx = np.random.choice(n, size=n, replace=True) score = metrics.brier_score(survival_train, survival_test[idx], - predictions[idx], horizon)[1] + predictions[idx], horizons)[1] scores.append(score) return scores elif strategy == 'integrated': horizon_scores = [] - for time in horizon: + for horizon in horizons: times = np.unique(outcomes_test['time']) - times = times[timesthres][0], y[y>thres][0] + root = fsolve(func, x0=x1, args=(thres, x1, y1, x2, y2))[0] + return root + def func(x, y, x1, y1, x2, y2): + return y1 + (x-x1)*((y2-y1)/(x2-x1)) - y + + if random_seed is not None: + treated_outcomes = treated_outcomes.sample(n=int(size_bootstrap*len(treated_outcomes)), + weights=treated_weights, + random_state=random_seed, replace=True) + control_outcomes = control_outcomes.sample(n=int(size_bootstrap*len(control_outcomes)), + weights=control_weights, + random_state=random_seed, replace=True) - treatment_survival = KaplanMeierFitter().fit(treated_outcomes['time'], - treated_outcomes['event']) + treated_survival = KaplanMeierFitter().fit(treated_outcomes['time'], + treated_outcomes['event']) control_survival = KaplanMeierFitter().fit(control_outcomes['time'], control_outcomes['event']) + treated_horizons = np.linspace(treated_outcomes.time.min(), + treated_outcomes.time.max(), + round((treated_outcomes.time.max()-treated_outcomes.time.min())*20)) + control_horizons = np.linspace(control_outcomes.time.min(), + control_outcomes.time.max(), + round((control_outcomes.time.max()-control_outcomes.time.min())*20)) + + treated_risk = 1-treated_survival.predict(treated_horizons, interpolate).values + control_risk = 1-control_survival.predict(control_horizons, interpolate).values + + tar_diff = [] + for risk in risks: + treated_tar = interp_x(treated_risk, treated_horizons, risk) + control_tar = interp_x(control_risk, control_horizons, risk) + tar_diff.append(treated_tar - control_tar) + + return np.array(tar_diff) + def _hazard_ratio(treated_outcomes, control_outcomes, treated_weights, control_weights, size_bootstrap=1.0, random_seed=None, **kwargs): diff --git a/auton_survival/phenotyping.py b/auton_survival/phenotyping.py index dd8f4ee..08b9513 100644 --- a/auton_survival/phenotyping.py +++ b/auton_survival/phenotyping.py @@ -31,6 +31,7 @@ from copy import deepcopy from sklearn import cluster, decomposition, mixture +from sklearn.metrics import auc from auton_survival.utils import _get_method_kwargs from auton_survival.experiments import CounterfactualSurvivalRegressionCV @@ -419,72 +420,171 @@ def fit_phenotype(self, features): return self.fit(features).phenotype(features) -class SurvivalVirtualTwinsPhenotyper(object): +class SurvivalVirtualTwinsPhenotyper(Phenotyper): - """"Not Yet Implemented""" + """Phenotyper that estimates the potential outcomes under treatment and + control using a counterfactual Deep Cox Proportional Hazards model, + followed by regressing the difference of the estimated counterfactual + Restricted Mean Survival Times using a Random Forest regressor.""" - - _VALID_PHENO_METHODS = ['rsf'] + _VALID_PHENO_METHODS = ['rfr'] _DEFAULT_PHENO_HYPERPARAMS = {} - _DEFAULT_PHENO_HYPERPARAMS['rsf'] = {'n_estimators': 50, + _DEFAULT_PHENO_HYPERPARAMS['rfr'] = {'n_estimators': 50, 'max_depth': 5} def __init__(self, cf_method='dcph', - phenotyping_method='rsf', + phenotyping_method='rfr', cf_hyperparams=None, phenotyper_hyperparams=None, random_seed=0): - - raise NotImplementedError() - - assert cf_method in CounterfactualSurvivalRegressionCV._VALID_CF_METHODS, "Invalid Counterfactual Method: "+cf_method - assert phenotyping_method in self._VALID_PHENO_METHODS, "Invalid Phenotyping Method: "+phenotyping_method + + assert cf_method in CounterfactualSurvivalRegressionCV._VALID_CF_METHODS, "\ + Invalid Counterfactual Method: "+cf_method + assert phenotyping_method in self._VALID_PHENO_METHODS, "Invalid Phenotyping Method:\ + "+phenotyping_method self.cf_method = cf_method self.phenotyping_method = phenotyping_method - if cf_method_hyperparams is None: - cf_method_hyperparams = {} + if cf_hyperparams is None: + cf_hyperparams = {} if phenotyper_hyperparams is None: phenotyper_hyperparams = {} - - phenotyper_hyperparams = deepcopy(SurvivalVirtualTwinsPhenotyper._DEFAULT_PHENO_HYPERPARAMS[phenotyping_method]).update(phenotyper_hyperparams) + self.phenotyper_hyperparams = phenotyper_hyperparams - - cf_hyperparams = deepcopy(SurvivalVirtualTwinsPhenotyper._DEFAULT_PHENO_HYPERPARAMS[cf_method]).update(cf_hyperparams) self.cf_hyperparams = cf_hyperparams self.random_seed = random_seed def fit(self, features, outcomes, interventions, horizon): + + """Fit a counterfactual model and regress the difference of the estimated + counterfactual RMST using a Random Forest regressor. + + Parameters + ----------- + features: pd.DataFrame + A pandas dataframe with rows corresponding to individual samples + and columns as covariates. + outcomes : pd.DataFrame + A pandas dataframe with rows corresponding to individual samples + and columns 'time' and 'event'. + treatment_indicator : np.array + Boolean numpy array of treatment indicators. True means individual + was assigned a specific treatment. + horizon : np.float + The event horizon at which to compute the counterfacutal RMST for + regression. - raise NotImplementedError() + Returns + ----------- + Trained instance of Survival Virtual Twins Phenotyer. + + """ - cf_model = CounterfactualSurvivalRegressionCV(**self.cf_method_hyperparams) + cf_model = CounterfactualSurvivalRegressionCV(model=self.cf_method, + hyperparam_grid=self.cf_hyperparams) self.cf_model = cf_model.fit(features, outcomes, interventions) - times = np.unique(outcomes.times.values) + times = np.unique(outcomes.time.values) cf_predictions = self.cf_model.predict_counterfactual_survival(features, - interventions, - times) + times.tolist()) ite_estimates = cf_predictions[1] - cf_predictions[0] + ite_estimates = [estimate[times < horizon] for estimate in ite_estimates] + times = times[times < horizon] + # Compute rmst for each sample based on user-specified event-horizon + rmst = np.array([auc(times, i) for i in ite_estimates]) - if self.phenotyping_method == 'rsf': + if self.phenotyping_method == 'rfr': from sklearn.ensemble import RandomForestRegressor - pheno_model = RandomForestRegressor(**self.phenotyping_method_hyperparams) - pheno_model.fit(features.values, ite_estimates) + pheno_model = RandomForestRegressor(**self.phenotyper_hyperparams) + pheno_model.fit(features.values, rmst) self.pheno_model = pheno_model + self.fitted = True + + return self + + def predict_proba(self, features): + + """Estimate the probability that the treatment group RMST is greater than + that of the control group. + + Parameters + ----------- + features: pd.DataFrame + a pandas dataframe with rows corresponding to individual samples + and columns as covariates. + + Returns + ----------- + np.array + a numpy array of the phenogroup probabilties. + + """ + + phenotype_preds= self.pheno_model.predict(features) + preds_surv_greater = (phenotype_preds - phenotype_preds.min()) / (phenotype_preds.max() - phenotype_preds.min()) + preds_surv_less = 1 - preds_surv_greater + preds = np.array([[preds_surv_less[i], preds_surv_greater[i]] + for i in range(len(features))]) + + return preds def predict(self, features): - raise NotImplementedError() + """Predict phenogroups. + + Parameters + ----------- + features: pd.DataFrame + a pandas dataframe with rows corresponding to individual samples + and columns as covariates. + Returns + ----------- + np.array + a numpy array of the phenogroup labels + + """ + phenotype_preds= self.pheno_model.predict(features) - phenotype_preds = (phenotype_preds - phenotype_preds.min()) / (phenotype_preds.max() - phenotype_preds.min()) - return phenotype_preds + preds_surv_greater = (phenotype_preds - phenotype_preds.min()) / (phenotype_preds.max() - phenotype_preds.min()) + preds_surv_less = 1 - preds_surv_greater + preds = np.array([[preds_surv_less[i], preds_surv_greater[i]] + for i in range(len(features))]) + + return np.argmax(preds, axis=1) + + def fit_predict(self, features, outcomes, interventions, horizon): + + """Fit and perform phenotyping on a given dataset. + + Parameters + ----------- + features: pd.DataFrame + A pandas dataframe with rows corresponding to individual samples + and columns as covariates. + outcomes : pd.DataFrame + A pandas dataframe with rows corresponding to individual samples + and columns 'time' and 'event'. + treatment_indicator : np.array + Boolean numpy array of treatment indicators. True means individual + was assigned a specific treatment. + horizon : np.float + The event horizon at which to compute the counterfacutal RMST for + regression. + + Returns + ----------- + np.array + a numpy array of the phenogroup labels. + + """ + + return self.fit(features, outcomes, interventions, horizon).predict(features) \ No newline at end of file diff --git a/examples/Demo of CMHE on Synthetic Data.ipynb b/examples/Demo of CMHE on Synthetic Data.ipynb index a4160b2..0b284db 100644 --- a/examples/Demo of CMHE on Synthetic Data.ipynb +++ b/examples/Demo of CMHE on Synthetic Data.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "7e3e61af", + "id": "5ded7b8d", "metadata": {}, "source": [ "# Deep Cox Mixtures with Heterogenous Effects (CMHE) Demo\n", @@ -25,30 +25,26 @@ "### 2. [Synthetic Data](#syndata) \n", "####               2.1 [Generative Process for the Synthetic Dataset.](#gensyndata)\n", "####               2.2 [Loading and Visualizing the Dataset.](#vissyndata)\n", - "####               2.2 [Split Dataset into Train and Test.](#splitdata)\n", + "####               2.3 [Split Dataset into Train and Test.](#splitdata)\n", "\n", " \n", "### 3. [Counterfactual Phenotyping](#phenotyping)\n", "\n", "####               3.1 [Phenotyping with CMHE](#phenocmhe)\n", - "\n", - "####               3.1 [Comparison with Clustering](#clustering)\n", - "\n", + "####               3.2 [Phenotyping with Virtual Twins Survival Regression](#vtsp)\n", + "####               3.3 [Comparison with Clustering](#clustering)\n", "\n", "\n", "### 4. [Factual Regression](#regression)\n", - "\n", "####               4.1 [Factual Regression with CMHE](#regcmhe)\n", - "\n", - "\n", - "####               4.1 [Comparison with a Deep Cox Proportional Hazards Model](#deepcph)\n", + "####               4.2 [Comparison with a Deep Cox Proportional Hazards Model](#deepcph)\n", "\n", "
\n" ] }, { "cell_type": "markdown", - "id": "02e1d71c", + "id": "929af912", "metadata": {}, "source": [ "\n", @@ -78,7 +74,7 @@ }, { "cell_type": "markdown", - "id": "20d71b4e", + "id": "ec3e5eed", "metadata": {}, "source": [ "\n", @@ -88,8 +84,8 @@ }, { "cell_type": "code", - "execution_count": 18, - "id": "bbf84a5a", + "execution_count": 8, + "id": "4522fb58", "metadata": {}, "outputs": [], "source": [ @@ -105,7 +101,7 @@ }, { "cell_type": "markdown", - "id": "d7ba3d8a", + "id": "676d208e", "metadata": {}, "source": [ "\n", @@ -114,7 +110,7 @@ }, { "cell_type": "markdown", - "id": "bac7bba7", + "id": "6b126859", "metadata": {}, "source": [ "1. Features $x_1$, $x_2$ and the base survival phenotypes $Z$ are sampled from $\\texttt{scikit-learn's make_blobs(...)}$ function which generates isotropic Gaussian blobs:\n", @@ -133,8 +129,8 @@ }, { "cell_type": "code", - "execution_count": 19, - "id": "826af396", + "execution_count": 9, + "id": "45d328ce", "metadata": {}, "outputs": [ { @@ -244,7 +240,7 @@ "4 0.748930 " ] }, - "execution_count": 19, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -259,7 +255,7 @@ }, { "cell_type": "markdown", - "id": "83ca0fea", + "id": "72b3fb0d", "metadata": {}, "source": [ "\n", @@ -268,8 +264,8 @@ }, { "cell_type": "code", - "execution_count": 20, - "id": "cd1754ef", + "execution_count": 10, + "id": "2838615d", "metadata": {}, "outputs": [ { @@ -289,7 +285,7 @@ }, { "cell_type": "markdown", - "id": "7156670f", + "id": "b7bb0f4a", "metadata": {}, "source": [ "\n", @@ -298,8 +294,8 @@ }, { "cell_type": "code", - "execution_count": 21, - "id": "c65b81bb", + "execution_count": 11, + "id": "e58d6786", "metadata": {}, "outputs": [ { @@ -353,8 +349,8 @@ }, { "cell_type": "code", - "execution_count": 22, - "id": "0c2628c6", + "execution_count": 25, + "id": "2e419169", "metadata": {}, "outputs": [], "source": [ @@ -380,7 +376,7 @@ }, { "cell_type": "markdown", - "id": "8e54dfd5", + "id": "b1d39c9c", "metadata": {}, "source": [ "\n", @@ -389,7 +385,7 @@ }, { "cell_type": "markdown", - "id": "2c21b829", + "id": "8f947f28", "metadata": {}, "source": [ "\n", @@ -398,8 +394,8 @@ }, { "cell_type": "code", - "execution_count": 23, - "id": "8e2f5ff3", + "execution_count": 19, + "id": "755c82c0", "metadata": {}, "outputs": [], "source": [ @@ -408,9 +404,9 @@ "g = 2 # number of underlying treatment effect phenotypes.\n", "layers = [50, 50] # number of neurons in each hidden layer.\n", "\n", - "random_seed = 3\n", + "random_seed =10\n", "iters = 100 # number of training epochs\n", - "learning_rate = 0.001\n", + "learning_rate = 0.01\n", "batch_size = 128 \n", "vsize = 0.15 # size of the validation split\n", "patience = 3\n", @@ -419,8 +415,8 @@ }, { "cell_type": "code", - "execution_count": 24, - "id": "5fe68dfb", + "execution_count": 20, + "id": "267164ba", "metadata": { "scrolled": true }, @@ -429,7 +425,13 @@ "name": "stderr", "output_type": "stream", "text": [ - " 28%|██████████████████████████████▊ | 28/100 [00:15<00:41, 1.75it/s]\n" + " 0%| | 0/100 [00:00" ] @@ -534,9 +536,352 @@ "plot_phenotypes_roc(outcomes_te, zeta_probs_test_CMHE[:, max_treat_idx_CMHE])" ] }, + { + "cell_type": "code", + "execution_count": 24, + "id": "ed3809ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2.1216183e-05, 9.9997878e-01],\n", + " [9.9602604e-01, 3.9739967e-03],\n", + " [9.9793184e-01, 2.0681797e-03],\n", + " ...,\n", + " [3.6357585e-04, 9.9963641e-01],\n", + " [1.0352042e-04, 9.9989653e-01],\n", + " [5.2588820e-01, 4.7411180e-01]], dtype=float32)" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zeta_probs_test_CMHE" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "9100d5fb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.9999788 , 0.003974 , 0.00206818, ..., 0.9996364 , 0.9998965 ,\n", + " 0.4741118 ], dtype=float32)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zeta_probs_test_CMHE[:, max_treat_idx_CMHE]" + ] + }, + { + "cell_type": "markdown", + "id": "87c7bc1c", + "metadata": {}, + "source": [ + "\n", + "### 3.2 Phenotyping with Virtual Twins Survival Regression" + ] + }, { "cell_type": "markdown", - "id": "90471bba", + "id": "b4a0e802", + "metadata": {}, + "source": [ + "A Virtual Twins model as first proposed in [1] predicts response probabilities for each sample using models trained separately on treatment and control groups. `auton-survival` fits a counterfactual model and regresses the difference of the estimated RMST using a Random Forest regressor.\n", + "\n", + "*For more information on Virtual Twins models [1], please refer to the following paper*:\n", + "\n", + "[1] [Subgroup identification from randomized clinical trial data](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3880775/)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "a37bbb0c", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/1 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Now let us evaluate our performance\n", + "plot_phenotypes_roc(outcomes_tr, phenogroups)" + ] + }, + { + "cell_type": "markdown", + "id": "e5e36371", "metadata": {}, "source": [ "\n", @@ -545,7 +890,7 @@ }, { "cell_type": "markdown", - "id": "5e4b3b79", + "id": "555a7366", "metadata": {}, "source": [ "We compare the ability of CMHE against dimensionality reduction followed by clustering for counterfactual phenotyping. Specifically, we first perform dimensionality reduction of the input confounders, $\\mathbf{x}$, followed by clustering. Due to a small number of confounders in the synthetic data, in the following experiment, we directly perform clustering using a Gaussian Mixture Model (GMM) with 2 components and diagonal covariance matrices." @@ -553,8 +898,8 @@ }, { "cell_type": "code", - "execution_count": 29, - "id": "08816e3e", + "execution_count": null, + "id": "de6e8dd9", "metadata": {}, "outputs": [], "source": [ @@ -570,41 +915,16 @@ "phenotyper = ClusteringPhenotyper(clustering_method=clustering_method, \n", " dim_red_method=dim_red_method, \n", " n_components=n_components, \n", - " n_clusters=n_clusters)" + " n_clusters=n_clusters,\n", + " random_seed=36) " ] }, { "cell_type": "code", - "execution_count": 30, - "id": "2e3e5bec", + "execution_count": null, + "id": "eaa74cbe", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "No Dimensionaity reduction specified...\n", - " Proceeding to learn clusters with the raw features...\n", - "Fitting the following Clustering Model:\n", - " GaussianMixture(covariance_type='diag', n_components=3, random_state=0)\n", - "Distribution of individuals in each treatement phenotype in the training data: [1306 1162 1431]\n", - "\n", - "Group 2 has the maximum restricted mean survival time on the training data!\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\Willa Potosnak\\miniconda3\\envs\\localenv\\lib\\site-packages\\lifelines\\fitters\\__init__.py:204: ApproximationWarning: Approximating using linear interpolation`.\n", - "\n", - " warnings.warn(\"Approximating using linear interpolation`.\\n\", exceptions.ApproximationWarning)\n", - "C:\\Users\\Willa Potosnak\\miniconda3\\envs\\localenv\\lib\\site-packages\\lifelines\\fitters\\__init__.py:204: ApproximationWarning: Approximating using linear interpolation`.\n", - "\n", - " warnings.warn(\"Approximating using linear interpolation`.\\n\", exceptions.ApproximationWarning)\n" - ] - } - ], + "outputs": [], "source": [ "zeta_probs_train = phenotyper.fit_phenotype(features_tr.values)\n", "zeta_train = np.argmax(zeta_probs_train, axis=1)\n", @@ -618,7 +938,7 @@ }, { "cell_type": "markdown", - "id": "f5dd6035", + "id": "782f9ed5", "metadata": {}, "source": [ "### Evaluate Clustering Phenotyper on Test Data" @@ -626,28 +946,10 @@ }, { "cell_type": "code", - "execution_count": 31, - "id": "028bef76", + "execution_count": null, + "id": "be4e1558", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Distribution of individuals in each treatement phenotype in the test data: [380 330 391]\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Now for each individual in the test data, let's find the probability that \n", "# they belong to the max treatment effect group\n", @@ -664,7 +966,7 @@ }, { "cell_type": "markdown", - "id": "3dd82f69", + "id": "8d8062d9", "metadata": {}, "source": [ "\n", @@ -673,7 +975,7 @@ }, { "cell_type": "markdown", - "id": "1ae0a9f1", + "id": "25baff78", "metadata": {}, "source": [ "For completeness, we further evaluate the performance of CMHE in estimating factual risk over multiple time horizons using the standard survival analysis metrics, including: \n", @@ -694,7 +996,7 @@ }, { "cell_type": "markdown", - "id": "9c009a2c", + "id": "81aaa75b", "metadata": {}, "source": [ "\n", @@ -704,19 +1006,10 @@ }, { "cell_type": "code", - "execution_count": 32, - "id": "80b4a724", + "execution_count": null, + "id": "70503c63", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Concordance Index (1 Year): 0.6548 (3 Year) 0.6656: (5 Year): 0.6665\n", - "Integrated Brier Score: 0.1597\n" - ] - } - ], + "outputs": [], "source": [ "horizons = [1, 3, 5]\n", "\n", @@ -729,42 +1022,9 @@ "print(f'Integrated Brier Score: {np.around(IBS, 4)}')" ] }, - { - "cell_type": "code", - "execution_count": 33, - "id": "63c26319", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0.1487453 , 1.8924836 , 0.19525401, ..., 0.4836966 ,\n", - " 0.33955073, 0.37479353],\n", - " [ 2.2802675 , -0.73033774, -1.7158557 , ..., 0.47515342,\n", - " 0.8169348 , 0.59493804],\n", - " [ 1.4902165 , -0.91186345, 0.7905248 , ..., 0.08489922,\n", - " 0.37587634, 0.62569857],\n", - " ...,\n", - " [ 2.0787652 , -2.0501418 , -0.36273366, ..., 0.63403505,\n", - " 0.97710544, 0.81890947],\n", - " [-1.5852283 , -0.79666543, 0.9420089 , ..., 0.6343607 ,\n", - " 0.5449456 , 0.01124444],\n", - " [ 0.25414062, 1.5835027 , -0.41139466, ..., 0.369376 ,\n", - " 0.05497523, 0.5677395 ]], dtype=float32)" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x_te" - ] - }, { "cell_type": "markdown", - "id": "a247dd6d", + "id": "0f284076", "metadata": {}, "source": [ "\n", @@ -773,20 +1033,12 @@ }, { "cell_type": "code", - "execution_count": 34, - "id": "4b944e83", + "execution_count": null, + "id": "3aaf3504", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 94%|████████████████████████████████████████████████████████████████████████████████████████████████████████▎ | 47/50 [00:03<00:00, 14.81it/s]\n" - ] - } - ], + "outputs": [], "source": [ "from auton_survival.estimators import SurvivalModel\n", "\n", @@ -804,7 +1056,7 @@ }, { "cell_type": "markdown", - "id": "bf5ee5fe", + "id": "b4b93e25", "metadata": {}, "source": [ "### Evaluate DCPH on Test Data" @@ -812,16 +1064,19 @@ }, { "cell_type": "code", - "execution_count": 35, - "id": "0d3cf5e9", + "execution_count": 1, + "id": "487dcfc1", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Concordance Index (1 Year): 0.6919 (3 Year) 0.6947: (5 Year): 0.6981\n", - "Integrated Brier Score: 0.153\n" + "ename": "NameError", + "evalue": "name 'dcph_model' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;31m# Find suvival scores in the test data\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mpredictions_test_DCPH\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdcph_model\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpredict_survival\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfeatures_te_dcph\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mhorizons\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m CI1, CI3, CI5, IBS = factual_evaluate((x_tr, t_tr, e_tr, a_tr), (x_te, t_te, e_te, a_te), \n\u001b[0;32m 5\u001b[0m horizons, predictions_test_DCPH)\n", + "\u001b[1;31mNameError\u001b[0m: name 'dcph_model' is not defined" ] } ], @@ -838,7 +1093,117 @@ { "cell_type": "code", "execution_count": null, - "id": "a792ce60", + "id": "8eb2a718", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "49d0f1f7", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import torch\n", + "from tqdm import tqdm \n", + "import sys\n", + "sys.path.append('../')\n", + "from auton_survival.datasets import load_dataset\n", + "from cmhe_demo_utils import * \n", + "\n", + "# Load the synthetic dataset\n", + "outcomes, features, interventions = load_dataset(dataset='SYNTHETIC')\n", + "\n", + "x = features.iloc[:100]\n", + "y = pd.DataFrame(outcomes, columns=['event', 'time']).iloc[:100]\n", + "#y = outcomes.iloc[:100]\n", + "i = interventions.astype('float64').iloc[:100]\n", + "#i = interventions.iloc[:100]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fda4247", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fa511f30", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/1 [00:00\n" ] }, { "cell_type": "markdown", - "id": "d3949e98", + "id": "e984fe01", "metadata": {}, "source": [ "\n", @@ -52,7 +53,7 @@ }, { "cell_type": "markdown", - "id": "6ec156cf", + "id": "636416cc", "metadata": {}, "source": [ "`auton-survival` offers utilities to phenotype, or group, samples for use in assessing differential survival probabilities across groups. Phenotyping can aid clinical decision makers by offering insight into groups of patients for which differential survival probabilities exist. This insight can influence clinical practices applied to these groups.\n", @@ -68,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "eda5bb46", + "id": "24d8e2be", "metadata": {}, "source": [ "\n", @@ -78,7 +79,7 @@ }, { "cell_type": "markdown", - "id": "6bf472e1", + "id": "fc152936", "metadata": {}, "source": [ "*For the original datasource, please refer to the following [website](https://biostat.app.vumc.org/wiki/Main/SupportDesc).*\n", @@ -88,8 +89,8 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "50b7cf7f", + "execution_count": 2, + "id": "4a479b1e", "metadata": {}, "outputs": [], "source": [ @@ -102,7 +103,7 @@ }, { "cell_type": "markdown", - "id": "21c52d23", + "id": "6ea602d5", "metadata": {}, "source": [ "\n", @@ -111,8 +112,8 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "7bc0c498", + "execution_count": 3, + "id": "edbd1c2c", "metadata": {}, "outputs": [ { @@ -400,7 +401,7 @@ }, { "cell_type": "markdown", - "id": "598804cf", + "id": "0ff79b8f", "metadata": {}, "source": [ "Here we perform imputation and scaling on the entire dataset but in practice we recommend that preprocessing tools be fitted solely to training data." @@ -408,8 +409,8 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "61e2f725", + "execution_count": 4, + "id": "ae30d5f4", "metadata": {}, "outputs": [], "source": [ @@ -422,8 +423,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "1445e99a", + "execution_count": 5, + "id": "823bab64", "metadata": {}, "outputs": [ { @@ -448,7 +449,7 @@ }, { "cell_type": "markdown", - "id": "bb8354d1", + "id": "3d407452", "metadata": {}, "source": [ "\n", @@ -457,7 +458,7 @@ }, { "cell_type": "markdown", - "id": "bb77eff8", + "id": "03430868", "metadata": {}, "source": [ "The intersectional Phenotyper performs an exhaustive cartesian product on the user-specified set of categorical and numerical variables to obtain the phenotypes. Numeric variables are binned based on user-specified quantiles." @@ -465,7 +466,7 @@ }, { "cell_type": "markdown", - "id": "2298969c", + "id": "449b1e4f", "metadata": {}, "source": [ "\n", @@ -474,7 +475,7 @@ }, { "cell_type": "markdown", - "id": "5f117fdd", + "id": "a9c7397a", "metadata": {}, "source": [ "Here we fit the phenotyper on the entire dataset but in practice we recommend that the phenotyper be fitted solely to training data." @@ -482,8 +483,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "83e21fcb", + "execution_count": 6, + "id": "2db693a2", "metadata": {}, "outputs": [ { @@ -496,7 +497,7 @@ " dtype='\n", @@ -528,7 +529,7 @@ { "cell_type": "code", "execution_count": 6, - "id": "33ffa96e", + "id": "b6685ba0", "metadata": {}, "outputs": [ { @@ -557,7 +558,7 @@ }, { "cell_type": "markdown", - "id": "68ff0142", + "id": "a9a5b347", "metadata": {}, "source": [ "As you can see, patients ages 18 to 64 without cancer have the highest survival rates. Alternatively, patients ages 64 to 101 with metastatic cancer have the lowest survival rates." @@ -565,7 +566,7 @@ }, { "cell_type": "markdown", - "id": "7061f353", + "id": "0639e04a", "metadata": {}, "source": [ "\n", @@ -574,7 +575,7 @@ }, { "cell_type": "markdown", - "id": "ff594be0", + "id": "645e23c4", "metadata": {}, "source": [ "Dimensionality reduction of the input covariates, $\\mathbf{x}$, is performed followed by clustering. Learned clusters are considered phenotypes and used to group samples based on similarity in the covariate space. The estimated probability of sample cluster association is computed as the sample distance to a cluster center normalized by the sum of distances to other clusters.\n", @@ -588,7 +589,7 @@ }, { "cell_type": "markdown", - "id": "bd475419", + "id": "73e8d1f8", "metadata": {}, "source": [ "\n", @@ -597,7 +598,7 @@ }, { "cell_type": "markdown", - "id": "bcf510b1", + "id": "d7bace22", "metadata": {}, "source": [ " " @@ -606,7 +607,7 @@ { "cell_type": "code", "execution_count": 7, - "id": "2c9b5abd", + "id": "003e3213", "metadata": {}, "outputs": [ { @@ -654,7 +655,7 @@ }, { "cell_type": "markdown", - "id": "7701e2ba", + "id": "9f706f8e", "metadata": {}, "source": [ "\n", @@ -663,8 +664,8 @@ }, { "cell_type": "code", - "execution_count": 8, - "id": "fb8d3fb3", + "execution_count": 6, + "id": "f6557355", "metadata": {}, "outputs": [ { @@ -694,7 +695,7 @@ }, { "cell_type": "markdown", - "id": "3d7ed290", + "id": "161418c6", "metadata": {}, "source": [ "Intersecting survival rates indicate that the SUPPORT dataset follows non-proportional hazards which violates assumptions of the Cox Model." @@ -702,7 +703,7 @@ }, { "cell_type": "markdown", - "id": "1a19711f", + "id": "b36d1092", "metadata": {}, "source": [ "\n", @@ -711,7 +712,7 @@ }, { "cell_type": "markdown", - "id": "b9c39451", + "id": "740ca8ad", "metadata": {}, "source": [ "To measure a phenotyper's ability to extract subgroups with differential survival rates, we estimate the (Integrated) Brier Score by fitting a Kaplan-Meier estimator within each phenogroup and employing it to estimate the survival rate within each phenogroup. We refer to this as the *phenotyping purity.*" @@ -719,8 +720,8 @@ }, { "cell_type": "code", - "execution_count": 9, - "id": "9d26d606", + "execution_count": 12, + "id": "6a6fbe40", "metadata": {}, "outputs": [ { @@ -734,7 +735,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\Willa Potosnak\\OneDrive\\Documents\\CMU Research\\CMU_Projects\\auton-survival\\examples\\..\\auton_survival\\metrics.py:260: UserWarning: You are are estimating survival probabilities for the same dataset used to estimate the censoring distribution.\n", + "C:\\Users\\Willa Potosnak\\OneDrive\\Documents\\CMU Research\\CMU_Projects\\auton-survival\\examples\\..\\auton_survival\\metrics.py:261: UserWarning: You are are estimating survival probabilities for the same dataset used to estimate the censoring distribution.\n", " warnings.warn(\"You are are estimating survival probabilities for \\\n", "C:\\Users\\Willa Potosnak\\miniconda3\\envs\\localenv\\lib\\site-packages\\lifelines\\fitters\\__init__.py:204: ApproximationWarning: Approximating using linear interpolation`.\n", "\n", @@ -748,7 +749,7 @@ "# Estimate the Integrated Brier Score at event horizons of 1, 2 and 5 years.\n", "metric = phenotype_purity(phenotypes_train=phenotypes, outcomes_train=y_tr, \n", " phenotypes_test=None, outcomes_test=None,\n", - " strategy='instantaneous', horizon=[365, 730, 1825], \n", + " strategy='instantaneous', horizons=[365, 730, 1825], \n", " bootstrap=None)\n", "\n", "print(f'Phenotyping purity for event horizon of 1 year: {metric[0]} | 2 years: {metric[1]} | 5 years: {metric[2]}')" @@ -756,7 +757,7 @@ }, { "cell_type": "markdown", - "id": "978c303b", + "id": "1c2e576c", "metadata": {}, "source": [ "\n", @@ -770,7 +771,7 @@ } }, "cell_type": "markdown", - "id": "296b1097", + "id": "5cd0b913", "metadata": {}, "source": [ "\n", @@ -790,7 +791,7 @@ }, { "cell_type": "markdown", - "id": "744aa357", + "id": "5d0ce8ae", "metadata": {}, "source": [ "\n", @@ -799,7 +800,7 @@ }, { "cell_type": "markdown", - "id": "b093362e", + "id": "506ebbed", "metadata": {}, "source": [ "Fit DCM model to training data. Perform hyperparameter tuning by selecting model parameters that minimize the brier score computed for the validation set.\n", @@ -813,7 +814,7 @@ { "cell_type": "code", "execution_count": 14, - "id": "cec70532", + "id": "51bb7267", "metadata": {}, "outputs": [ { @@ -857,7 +858,7 @@ }, { "cell_type": "markdown", - "id": "00068611", + "id": "5253ad49", "metadata": {}, "source": [ "\n", @@ -870,7 +871,7 @@ { "cell_type": "code", "execution_count": 15, - "id": "1c37f5ac", + "id": "b3783f2d", "metadata": {}, "outputs": [ { @@ -899,7 +900,7 @@ }, { "cell_type": "markdown", - "id": "3e213acf", + "id": "f8a1067f", "metadata": {}, "source": [ "\n", @@ -909,7 +910,7 @@ { "cell_type": "code", "execution_count": 16, - "id": "724422d6", + "id": "d91f5ab9", "metadata": {}, "outputs": [ { @@ -938,7 +939,7 @@ }, { "cell_type": "markdown", - "id": "b8bb50c2", + "id": "ef7be826", "metadata": {}, "source": [ "Intersecting survival rates indicate that the SUPPORT dataset follows non-proportional hazards which violates assumptions of the Cox Model." @@ -946,7 +947,7 @@ }, { "cell_type": "markdown", - "id": "9d460add", + "id": "aa407976", "metadata": {}, "source": [ "\n", @@ -955,7 +956,7 @@ }, { "cell_type": "markdown", - "id": "efaf0ea0", + "id": "b933a72b", "metadata": {}, "source": [ "To measure a phenotyper's ability to extract subgroups with differential survival rates, we estimate the (Integrated) Brier Score by fitting a Kaplan-Meier estimator within each phenogroup and employing it to estimate the survival rate within each phenogroup. We refer to this as the *phenotyping purity.*" @@ -964,7 +965,7 @@ { "cell_type": "code", "execution_count": 17, - "id": "adfaac68", + "id": "0a4ed63e", "metadata": {}, "outputs": [ { @@ -992,7 +993,7 @@ "# Estimate the Integrated Brier Score at event horizons of 1, 2 and 5 years\n", "metric = phenotype_purity(phenotypes_train=phenotypes, outcomes_train=y_tr, \n", " phenotypes_test=None, outcomes_test=None,\n", - " strategy='instantaneous', horizon=[365, 730, 1825], \n", + " strategy='instantaneous', horizons=[365, 730, 1825], \n", " bootstrap=None)\n", "\n", "print(f'Phenotyping purity for event horizon of 1 year: {metric[0]} | 2 years: {metric[1]} | 5 years: {metric[2]}')" @@ -1000,7 +1001,7 @@ }, { "cell_type": "markdown", - "id": "850e812b", + "id": "ead8730c", "metadata": {}, "source": [ "It can be observed the phenotyping purity is lower for supervised phenotyping compared to unsupervised phenotyping. This indicates that the supervised phenotyper is able extract phenogroups with higher discriminative power in terms of the observed survival rates.\n", @@ -1010,16 +1011,16 @@ }, { "cell_type": "markdown", - "id": "69bcb6e4", + "id": "ea3046ef", "metadata": {}, "source": [ - "\n", + "\n", "## 5. Counterfactual Phenotyping" ] }, { "cell_type": "markdown", - "id": "ba772dea", + "id": "ee6bdffd", "metadata": {}, "source": [ "*For examples of counterfactual phenotyping with Deep Cox Mixtures with Heterogeneous Effects (CMHE) [1], please refer to the following paper and example jupyter notebook*:\n", @@ -1032,7 +1033,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b4756c39", + "id": "81df3294", "metadata": {}, "outputs": [], "source": [] diff --git a/examples/Survival Regression with Auton-Survival.ipynb b/examples/Survival Regression with Auton-Survival.ipynb index b436c85..cdd2897 100644 --- a/examples/Survival Regression with Auton-Survival.ipynb +++ b/examples/Survival Regression with Auton-Survival.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "1fa2753c", + "id": "d7efe2f9", "metadata": {}, "source": [ "# Survival Regression with `estimators.SurvivalModel`\n", @@ -47,7 +47,7 @@ }, { "cell_type": "markdown", - "id": "b8b5cbe0", + "id": "85369414", "metadata": {}, "source": [ "\n", @@ -57,7 +57,7 @@ }, { "cell_type": "markdown", - "id": "dfa5ae91", + "id": "cd41f8b9", "metadata": {}, "source": [ "The `SurvivalModels` class offers a steamlined approach to train two `auton-survival` models and three baseline survival models for right-censored time-to-event data. The fit method requires the same inputs across all five models, however, model parameter types vary and must be defined and tuned for the specified model.\n", @@ -103,7 +103,7 @@ }, { "cell_type": "markdown", - "id": "7762a8bf", + "id": "c2099ee9", "metadata": {}, "source": [ "\n", @@ -113,7 +113,7 @@ }, { "cell_type": "markdown", - "id": "453efa17", + "id": "87369d9d", "metadata": {}, "source": [ "*For the original datasource, please refer to the following [website](https://biostat.app.vumc.org/wiki/Main/SupportDesc).*\n", @@ -123,8 +123,8 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "78ceb1eb", + "execution_count": 2, + "id": "26e6b532", "metadata": {}, "outputs": [], "source": [ @@ -137,8 +137,8 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "edfdc7cb", + "execution_count": 3, + "id": "532b3549", "metadata": {}, "outputs": [ { @@ -426,7 +426,7 @@ }, { "cell_type": "markdown", - "id": "4eb21858", + "id": "f2e9776e", "metadata": {}, "source": [ "\n", @@ -435,8 +435,8 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "0d108642", + "execution_count": 4, + "id": "de6456e1", "metadata": {}, "outputs": [ { @@ -464,8 +464,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "b69dc7da", + "execution_count": 5, + "id": "38684f59", "metadata": {}, "outputs": [], "source": [ @@ -482,7 +482,7 @@ }, { "cell_type": "markdown", - "id": "2e937840", + "id": "00683f5e", "metadata": {}, "source": [ "\n", @@ -491,7 +491,7 @@ }, { "cell_type": "markdown", - "id": "d0713974", + "id": "34a6c3f7", "metadata": {}, "source": [ "CPH [2] model assumes that individuals across the population have constant proportional hazards overtime. In this model, the estimator of the survival function conditional on $X, S(·|X) , P(T > t|X)$, is assumed to have constant proportional hazard. Thus, the relative proportional hazard between individuals is constant across time.\n", @@ -503,7 +503,7 @@ }, { "cell_type": "markdown", - "id": "44ff4aad", + "id": "be77b767", "metadata": {}, "source": [ "\n", @@ -512,8 +512,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "32552d7d", + "execution_count": 7, + "id": "41c56557", "metadata": {}, "outputs": [], "source": [ @@ -525,7 +525,7 @@ "param_grid = {'l2' : [1e-3, 1e-4]}\n", "params = ParameterGrid(param_grid)\n", "\n", - "# Define the times for tuning the model hyperparameters and for evaluating the model\n", + "# Define the times for model evaluation\n", "times = np.quantile(y_tr['time'][y_tr['event']==1], np.linspace(0.1, 1, 10)).tolist()\n", "\n", "# Perform hyperparameter tuning \n", @@ -538,7 +538,7 @@ "\n", " # Obtain survival probabilities for validation set and compute the Integrated Brier Score \n", " predictions_val = model.predict_survival(x_val, times)\n", - " metric_val = survival_regression_metric('ibs', y_tr, y_val, predictions_val, times)\n", + " metric_val = survival_regression_metric('ibs', y_tr, predictions_val, times, y_val)\n", " models.append([metric_val, model])\n", " \n", "# Select the best model based on the mean metric value computed for the validation set\n", @@ -549,7 +549,7 @@ }, { "cell_type": "markdown", - "id": "6b697d6c", + "id": "630de5a1", "metadata": {}, "source": [ "\n", @@ -558,15 +558,15 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "cfde11cc", + "execution_count": 21, + "id": "62c21074", "metadata": {}, "outputs": [ { "data": { - "image/png": "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GnXNH+sazVQ6mzx5sj311FPuxy+99JINGjTIhg8f7rGXJi4uzv1m2LBhg/Xs2dOKFi1qPXv2tAYNGthrr73mKH5CQoIlJCRYWFiY/etf/zKzv97oTz/9tN199902Z84cj+UJCQnu///oo4/swQcftAoVKlivXr0sNDQ0TW/8qKgou+eee2zGjBnuZbNmzbKSJUvaTz/9lGy+NxL77x+amJgYW7hwoZ07d87mzp1rLpfLPW83cW9Eor9/Afz66682ZcoUe/jhh+3//u//7J133rlm7IsXL9rZs2fN7MoXmze3+3ri4uKsadOmtmrVqjQ97+DBg1aiRAlr1qyZrV692n7++Wdr0qSJTZ06Nc05fPnll9ahQwdr3bq1+zK93oh977332pIlS9yPDx06ZM2aNfPYe/n3P7gShYWFWenSpa1///7Wtm1ba9Omjcfn9u+ufv6pU6esUqVKtnz5cvey6Oho69Wrl9WvXz/J5XATP6uJnnzySStZsqT16dPHwsLCLCwszPbt25embb/azJkzLTQ01OMIwvLly83lctnYsWPt4sWL7jyu/iycOXPG6tWrZ/fee6+NGjXKWrZsaU888YQdP37ca/cIgn+hrlHXvLHd10Ndo675a12jmUpnQ4cOtRYtWti6deusbdu21q9fP1uyZImVKFHCKlSocM0pDtu2bbMPP/zQTp48maaYV3faZle+TJs0aWJ9+/a1CxcuuJfv2bPH6tata40bN3bHSG6PwsWLF+2bb76xd95557o3eUuMnfhmjImJsVq1alndunXt888/t6lTp9odd9xhDz74oC1atMgOHTrk3otzo7Hffvtt69Wrl7388sseXyiJJ0b//PPP7i/za0mcupIoNjbWYmJirvmcmTNnWp06dWzhwoVe3+7riY+Pt7Nnz1rLli3de4VPnz5tY8eOtenTp7tzNvPcm5z4O4yMjLTBgwdbixYtrGbNmu4/UlLj6i/SNWvWWLdu3WzBggXmcrnccePj493r3Gjs6OhoO3TokMeUkw0bNri/rM+cOWPNmze3OnXqWO/evW3cuHHum4deLbEgnD592tavX29Tpky5buyLFy96/C7/+OMPGz9+vM2dO9djvfHjx1ubNm08tvNqiX8MXbx40bZs2WLvv/++x548p9577z0rXLiwe69tYq5PPPGElShRItk9com5HD161FauXGnjx4+3jz/++IZzQeZGXaOuUdeoa1ejrnmimUpnP/zwg2XPnt2ee+45jz0PW7dutSZNmljlypXN7MovtX///u49LGntjBPXf+6558zlcrm/OBM/xBMmTLACBQok2Xv06quvWrly5Wz27NnuZdu3b7fRo0cnu6cpLbET37Dff/+9TZw40bp27WpVq1a1qKgoi4uLs4EDB1rlypU99oylNbbZlRtDPvroo9axY0d78803LVu2bNajR49kv0z++9//Wo4cOeztt9/2yDHRiy++aFWqVHF/OV/PN998Y+3atbOuXbtaVFSUx1hGb3dKZsyYYR988IFt2LDBY3nJkiXttddes82bN1uNGjVs0KBB9uyzz5rL5bI+ffrYkSNHPNb/5ZdfPB6fOnXqunPyp0yZYq+//rp9/vnnScbefvttmzx5spmZtWvXzooWLerx2jcae+rUqVapUiW7//77rXr16sn+sbZo0SLr3bu3bdu2zV588UULCQmxZ555xn3OgdmVaSNXT4FIjddff90aNmxoDz/8sMdNMM+cOeP+fCR+yS9btizF6S1OYv/duHHj3EXq6p/ZwYMHLVeuXPb++++bmbmLz4kTJywkJMSGDBni8cfVu+++a5s3b76hXJA1Udeoa9S1K6hr1LXk0ExlgPbt25vL5XKf2Jb4Znv33XetXLlytnPnTtu4caOVKFHCBg8e7DjOkSNHrGvXrnbnnXe6L3OZGOvIkSN266232gsvvODxZj99+rSVL1/eY475rFmzLHfu3DZz5sx0iZ3o8ccf95gecPr0aWvZsqXVrl3bvZdt5syZaYq9Z88ee+ihhzz2Krz99ttWrFgxj/USczlz5ow988wzdvPNN3t8IBP/f9WqVdaiRYtUnRT76aefWs2aNe3TTz/1WP73QpYR252clE7GTiwmzz//vN1+++02ceJEj4L06quvWqlSpTzOPejQoYM99NBD7jnH17N+/Xq7//77rWPHjjZixAjLli2b9e7d2+Pn+NVXX1nv3r3NzGzTpk128803W5cuXaxFixb2n//8x3Hsn376yVq3bm39+vWzAwcO2LZt26xgwYL24osvmlnK87fNzN58800LCQmxpUuXupdVrlzZateunew5AH+3e/dua968ufXu3dt27dplw4YNs9y5c3t8nsw8PwsDBw60CRMmmNmVk7Cv/qJPS+zk/PDDD1a0aFErV66c+4+uxO0/ceKEPfroo1a8eHH3+omx//Wvf1mpUqXcyy9evGi5cuWyzp07e+z1BxJR166grl1BXaOuUdf+QjOVAXbs2GEhISEWFhZmv/32m3t54gfvwIEDZmY3fOLbzJkzLTw83JYsWWJBQUH25Zdfusfi4uIsPDzccufObevXr/d4Xr9+/axu3boey7744ot0i212ZZ52yZIl3YfAEz+Eb7/9tuXNm9d974W0xk48ifnqK9NMnTrVwsPDU3zO999/b7fffrs999xzZmbXneqQkoMHD9q4ceM89nzFx8fbyZMn3YfTf/nlF7vzzjvTfbuTc72TsTdv3mwlSpQwl8tla9asca93+fJla9CggT3xxBPun8Xbb79tTZo08dizdS09evSw9957z/14wYIFVqBAAevatat72ciRI23KlClmdmWaQ6VKlSw4ONh9ha9EU6dOTVPsiRMnJjkZvmPHjjZ8+PAk6/592oWZWaFChWz8+PHux9u2bbvuHtzE548dO9bj6j9//vmnjRkzxvLnz5/kyzrxj5FOnTq5X//DDz/0mPe+devWVO89/rs//vjDOnToYB07dvT4Q/LqbV24cKHdfPPN7oKcmNP27dutQIEC9uOPP7rX/fLLLy0yMtJRLsj6qGvUNeoadY26ljyaqQwyduxYu+WWW9yHg82ufDC7d++ebh3y/v377dKlS3bhwgVr06aNVapUyeNkwOPHj1vVqlWtQYMGHntW3nrrLXv66afNLOnepxuJ/ffX+sc//mFt2rTxmPe7bt06q1ixYpJD8Wlx9U3W5s+fbzfffLM9/PDDVqdOHfv000/dJ88m5nPhwgV78803LTg42F599VXr3LlzkqkMqfXOO+/Y/Pnz7dKlSzZ//nxr0qSJtWvXzsqWLWuffPKJxcbGWu3ata1Vq1bpvt2Jrncydrly5WzBggVmduVch+DgYFu8eLElJCS43x/PP/+8Va9e3VH83bt329133+2+ilBi7LCwMHO5XO4rP3344Yc2fPhwGzlypD3wwAM2cuRIu/vuu9179VJ7ieS/7xl+77333HvEzK7ML2/cuLF99dVXHkU9OXFxcfZ///d/Nm3atFTFTrxJ6aRJk+z48eM2evRoe/DBBz22e926dVa1atUkJ/Qmfvk/8sgj7j2306dPt1tvvdXxHrur/f777zZ79my7ePGiPf/881asWDH3nPrE3/OpU6dswIABlj17do+pDps3b7Y6deqkeq8pYEZdM6OuUdeoa2bUtb+jmcpAiW+Erl27WuvWre2hhx6yTZs2pdvrX/1h3LBhg+XKlcveeOMNj3V27txpxYsXtxYtWtimTZts7969dv/999/QYfjrxU4cW7hwoblcLhsyZIgdP37cdu/ebY0aNbLnn3/ecbG7WkxMjI0dO9Y2btxoP/30k3Xt2tWKFSuW7N68+fPnW1BQkDVq1Mhjr0VanT171oYNG2b9+vWzDh062K5duywqKso6depkd9xxh7366qu2bNmydN9uJydjR0dH2+HDh61mzZpWo0YN92Vczcz69+/vsQcuNbETf6/R0dGWK1cue/PNN83sry+4d955x1wul9WsWdPOnj1rr7/+ut1zzz02ZswY96H6Dz74wFwul1WqVMmmTp3qcdJucq4+GTvxjwkzcxf0HTt2WNmyZa1Vq1b20EMPue/4fvToUTO78uWaeF6B2ZWCdd9997kv13sts2fPtgYNGlibNm2sadOmtnTpUjt79qx9/fXXHlcK2r9/vxUpUiTZSz7/+OOP9thjj9nu3bvt8ccftxw5cli7du3SvBc5pTn8iUUmIiLCatasaR07dkzy3EOHDtmDDz5od999t/uPkddff906dOiQ7PkYwLVQ16hr1DXqGnXNE81UBoqPj7edO3fawoUL0+UqJtcSGxtrAwYMsIIFC7rvQ5C4B2Hjxo323HPPWatWraxWrVoeV73JqNiJXnrpJatSpYrVq1fP7rvvvnSLndL84bCwMKtVq5bt3bvXzK58UW7ZssXq16+fbldvWbFihT311FP2ww8/eCxv3LixNWzY0M6cOeO+qdyNbPeNnIxdpkwZ99V39u/fb+XLl7dq1arZ+PHjrWXLlvb444/br7/+mubYiT/3f/7zn1a0aFGPvVavvPKK9ejRw2rXrm0fffSRRUdHe3y579q1y6pVq2Zt2rTxKIDJScvJ2InTi8zMJk2aZIULF3bPXY+IiLDixYtbv3797MEHH/S48ei1/P0mpXv37k32JqVxcXF28OBBCwsLcxfCq6cjvPvuu1a0aFHLkyePVapUKcl75npSmsOfuAc6MZ/Lly/b5MmTrUiRIu5L2F69N//SpUvWu3dva9y4sTVo0MAeeeSRJCdHA6lBXaOuUdeoa9Q1TzRTWcj+/futePHi1qtXr2TH03pp2vSKfeHChQy5mkriF2B8fLz7g75gwQLLkyePu+iYXf8u8WkVFxfn3hN79SVYZ86caXnz5nXPe7948eINb/eNnoyduMdw3759tmTJEhs+fLjH3Oa0xk50/PhxK1eunN1111324osvWs+ePa1Lly62detWq127tsd0hcTf05EjR1I1BSItJ2Nf/aWbqEaNGvbII4+Y2ZW9vJ999pmNHj06TXP5U3OT0sTf+4oVK6xbt25mduVzsG7dOved5ceMGWMFChRw/AdPaubwJ277rl27rHnz5knOG7n6Mr1nzpxJ1UnpgL+grlHXzKhr1DX/rms0U1nI5cuXbcqUKXbTTTfZ/v377auvvkrzHoP0jO3Nk9kT9+xs2LDB/vGPf1xz71R6Szy0HRERYTVq1EiyF/NGpOfJ2OkVO/HLd9++fTZz5kwbNWqUx1ztgQMH2sMPP+w4bmpPxv773rTEPWj9+/e3e+6554ZuxJfam5SamQ0bNsw9NWTXrl329NNPu0+MvpH34fXm8CcWo6v/qPrggw+sWLFi7t9H4mVjgcyKukZdS0Rdo66Z+Wddo5nKgsqXL2833XSTtWnT5ronLmb22M8995y7sO7fv98efPBBe/755695GdH0Mnv2bPfekIMHD1qzZs1s1KhR6RojPU7Gdvrlm5qTsZMTHh7uPkHd6e8htSdjm12ZX361zp0722uvveYobqLU3qQ0NjbWunbtat9++639/PPP1qdPH3O5XI7O3XAyh//33383s7/24v3666/Wu3dvu/vuu+3NN9+0gQMHuufZA5kZdY26Rl17zVHcRNS1jEMzlUUknjjYt29fq1Wrlr377rsBEfvbb7+122+/3Tp06GCNGjXyuGljRvvhhx+scuXK9vjjj1uTJk0yJLa/nox9tUOHDrmvnrN3715r27atrVu37oZiJ7reydi//fab3XPPPfbxxx/bd999Zx06dLC2bdvawYMHbyju9W5SWqlSJTO78mXfqlUr69+/vxUuXNjuvffeNF0a+kbm8NetWzfZz9qYMWPM5XLZQw89ZHv27En7xgN+grpGXaOuUdfM/L+u0UxlMfPmzfPZFbp8Ffvzzz+3GTNm+CR2RESEzZkzx/E9PtLCX0/G/vTTT61JkyZWv359a9SokddPxh4/frzVrVvX6tWrd8N3Xb/a9W5Sun37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\n", + "image/png": "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V65ccY8vW7asli9frn79+il//vxKSEjQp59+ygV7AQAAAOQIvwlTkhQZGanIyMh0182fPz/NsvDwcH311VfXuywAAAAASMNvDvOD7wQHB2vo0KFMzpEB+uMd/fGO/nhHf7yjP97RH+/oT8bojXf0J/NcxtVqAQAAACDL2DMFAAAAAA4QpgAAAADAAcIUAAAAADhAmAIAAAAABwhTAOAnmA8IgC/xHpQxepM1ealfhCmksWvXLv3yyy++LuOGcfDgQfebRl5688gMeuPdvHnzNGrUKH388ceSJJfL5eOK/AuvH+fol3f0x9Mvv/yiMWPGaO7cuXz+pyM+Pl6//vqr+zavH+8uX77s8XmW2/vlVxfthW998803euONN3TixAnFxsaqY8eOioqKUpkyZZSSkqKAALL3tTZs2KC33npLFy5cUEJCgl555RU1bdpUBQoUkJnl6S/G9Ma706dPq1OnTipatKjuuecePfnkk1q0aJFefPFF3Xfffb4uz+d4/WTd7NmzlZiYqKpVq6pu3br06HfoT8ZmzZqlWbNmKTQ0VLGxsapWrZruvfdeX5flNyZPnqwFCxaoRIkSCg4O1uTJk3XLLbf4uiy/NW7cOH322We64447VK5cOQ0fPjz3/70ZYGbx8fHWsmVLmzVrlpmZRUdHW/Xq1a1t27YWHx9vZmYpKSm+LNGvHD9+3B544AFbvny5Xbp0yV5//XUrXbq0DRo0yNel+Ry9+WNLly61Hj16uG9v2rTJqlWrZi1atLBz5875sDLf4/WTNfv377fw8HCLioqy5557zooUKWJDhgyxU6dOmRnv2/THu5iYGHv88cdt//79Zma2bds2+/nnn31clf+YOnWqRUZG2oEDB+zLL7+0KlWq2AMPPGBr1671dWl+58qVK/bqq69aly5d7ODBgzZ9+nQrXry4de/e3Y4cOeLr8q4rwhTM7OqXuXLlynksmzFjhpUrV8769OljZmbJyck+qMw/vf/++3bvvfd6LHvxxRftrrvusqlTp5pZ3u0XvfljI0aMsDp16piZWVJSkpld/XurWLGi++8tr+L1kzX/+te/bPDgwe7b8+bNs+LFi9srr7xiiYmJPqzMP9Af73r27Gljx471dRl+q0mTJjZz5kz37f3791vVqlWtVatW9uOPP/qwMv8TFxdnf//7323jxo3uZatWrbKbb77Z+vbta7/88osPq7u+OG4LkqSCBQsqICBAX375pXvZY489pvbt22vChAnas2ePAgICcv1xr5kVFBSk4OBgHTlyxL2sT58+qlq1qsaPH6+zZ8/m2X7Rmz+WkpKiAgUK6MCBAwoMDJQkdezYUY0bN9ZHH32kb7/91rcF+hCvn8xJ3f7t27fr1KlTkqTk5GRFRUXp8ccf1yeffKL33nvPY2xeQn+8S93mkJAQ93tQcnKyxo4dqzFjxmj8+PE6d+6cL0v0uUuXLsnlcunixYvuft11113q3bu39uzZo8mTJ/u4Qv9y7NgxXbx4Ub/88ovs6s4aNW7cWF27dtXSpUu1dOlSX5d43RCm8pjk5OR0l8fHxyskJETr1693LytcuLDat2+vatWqacCAAZI4Qf5aR48e1Xfffee+Xa5cOXXo0EHJycl6++23JeXdftGb9KV+INerV0/r16/Xtm3b3MtuvvlmtWvXTsWLF9d//vMfX5bpc7x+/ljq9sfGxsrM9PPPP7u/FL/88ssqXbq05s6dqxMnTuTJXtEf71K3+fjx49q2bZt27dqlJk2a6OTJkypSpIgGDhyoDh066KuvvvJxpb5TqFAhxcXFad26dfr555/dy3v06KEaNWrof//7n3bu3OnDCv1L5cqVdfHiRa1Zs0Yul8v9fXPIkCHKnz+/PvnkE/30008+rvL6IEzlEXFxcRo+fLjGjx+vo0ePpllfs2ZNlSpVSsuWLdPu3bvdy//2t7/pscce0549e/Lsr+Xp/Wr56KOPKigoSB988IEuXLjgXt6sWTOFhYVp8+bN7l9DczN6k7H0epP6BebBBx9UjRo1NH36dJ04ccK9vmHDhvrrX/+qQ4cO6ezZszlWq6/w+nEuJSVFkvTQQw9p4cKF7vd1M1OZMmX02GOP6eTJk1q+fLkvy7zuMtqrRH+8S/2i2717d3300UdasGCBevTooX//+9/q2bOnli9frvPnz+vNN9/0caW+kdqf559/Xp9++qm2b98u6errJzg4WE888YTOnz+vzZs3+7JMn0j920pvWdeuXTV9+nRduHBBQUFBSkpKUvHixdW1a1etWbMm3e+fuQFhKg+YM2eOmjVrpm+++UarV69O80tK6pvG8OHD9dVXX2nZsmWKj4+XdPWQm/DwcAUHB3v8MpObzZw5U++9956+/vprSWl/AU/t1+DBg7Vw4UJt2LDB/YFeqFAhPfjggzp8+HCu/LVz0qRJGj58uObOnSuJ3vzeuHHjNHLkSEnpf+BIv335Gz16tNasWaPFixe7p9x1uVxq3bq1tm7dqmLFiuVM0TmI10/WnDhxQmPHjtWmTZvSrEudXbVDhw4qXLiwpk6dqpSUFHdvunTpopCQEO3evVspKSm58lC2n376SUlJSZLS/r3l9f6cPHlSQ4cO1ezZs92f+df2KHUvXf369VW/fn2NGjXK4wecRo0a6aGHHtKxY8d0/PjxnC0+B5w6dUoLFizQzp073YczXnvkTmp/Un/4GjNmjH755Rf366dt27YqUKCAew96Ru/3ucWRI0c0Z84cHTx4UHFxcZI8tzn1761t27YKCAjQiBEjPJb369dPly9f1q5duyTlwkNrc+C8LPjQ4cOH7fHHH7fdu3ebmdn3339vly5dSjODUertqKgoq1ixoi1evNhjebFixezTTz/Nwcpz3s6dO61OnTrWqVMne/bZZ61QoUI2duxYO336tJmlPek9JSXFateubXXr1rXt27e7l589e9YKFSpke/fuzdH6r6e1a9dagwYNrHv37jZhwgRzuVz22muvZXhCaV7qTaozZ87YPffcYy6Xyz1TWOrkEhnp2rWr3XHHHbZkyRL3su3bt1ujRo0sNjY218w0xusn6958802rWbOm1a5d28LDwy02NjbdcSkpKfavf/3LgoOD7YsvvvBYN3ToUKtevXoOVJvzvv32W3O5XNa9e3czy3hSkrzYn/Hjx1tERIQ9/fTTVq9ePStUqJD9+uuvGY5funSpBQYG2quvvmqXLl1yL//444+tcOHCdv78+ZwoO8f861//stq1a1u7du2sTJky9tBDD7nXpfeeu2TJEgsICLApU6ZYfHy8e8xrr71mzZo1y7G6fSElJcVGjx5tNWrUsKioKKtVq5a1bds2w7+3y5cv2/Dhwy0wMNA2bNhgZldn+TMza9u2rfXv3z/Has9JhKlcbvDgwfbiiy96LEsvFKR+6Ttz5ozVrVvX6tev7/5DOHDggLVu3drOnDmTM0X7yIgRIzxmNZo+fbqVKFHCXnvtNfey1DfR1P/97rvvrFixYta9e3c7ePCgmV2dvaZ9+/a55otwSkqKtWvXzqZPn+5e9uabb9rNN99sx48fT3e8Wd7oTaorV67Yc889Z507d7a//vWv1rp1azPz/gXP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3XHHHdauXTsz+60fJ06csFtvvdX69+/v8aZ6/vx5q1y5sse5U9HR0VagQAF77733cnYDriNvvUn1+OOP2zvvvOO+ff78eWvRooXVqVPHvWfmvffey3W92b9/v7Vq1crj17Vp06bZ7bff7jEutV8XLlywnj172i233OLxwZT6359//rk1b94810yksGjRIgsLC7NFixZ5LP99iMyrr5/fy2iym9SQ9Oqrr1qpUqVs7NixHkHrrbfesvLly3uc29mhQwdr1aqV+/yF3GDTpk32wAMPWMeOHW3w4MEWFBRkvXv39vh7+eyzz6x3795mZrZlyxa75ZZbrHPnzta8eXP797//7R6X2/qzb98+a926tfXp08cOHTpkO3bssGLFitnrr79uZhmf82NmNnHiRAsODrbly5e7l9WoUcPq1KmT7vmLN6K9e/das2bNrHfv3rZ792577bXXrECBAh6f32aen22vvPKKjRkzxsyuTnJzbTjIbf1Jz9atW6106dJWqVIl94+Dqa+jM2fO2KOPPmplypRxj0/tz0svvWTly5d3L4+Li7PQ0FDr1KmTxxE+eQFhKg/47rvvLDg42CIjI+3HH390L0/9ADp06JCZWa49MfBa7733ng0fPtyWLl1qAQEB9t///te9LikpyYYPH24FChSwTZs2edyvT58+dv/993ss++STT3Kk5pzirTdmZj/88IOVK1fOfThN6ofRtGnTrGDBgrZz50732NzWm9RJWK6d6WrKlCk2fPjwDO/z9ddfW6lSpeyFF14wM8sVh/Bl5PDhwzZq1CiPPSbJycl29uxZ9yFEx44dszvuuCNPvn5+748mu9m2bZuVLVvWXC6XrV+/3j3uypUrVr9+fXviiSfcr6dp06ZZ48aNPfY23Oi6d+9u//nPf9y3FyxYYEWLFrUuXbq4lw0ZMsQmTZpkZlcP36tevboFBga6Z6JNNWXKlFzVn7Fjx6aZVKpjx442aNCgNGN/f7ijmVnx4sVt9OjR7ts7duzIFXvIU7dx5MiRHjPG/frrrzZixAgrXLhwmi/4qT/2REVFuXvw/vvv29KlS91jtm/fniv6k5Gff/7ZOnToYB07dvT40fja18yHH35ot9xyizuwp/Zt586dVrRoUfv222/dY//73/9aTExMDm6BfyBM5REjR460IkWKuA+LMLv6AdWtW7c89QvCwYMHLT4+3i5fvmxt2rSx6tWru7/smZmdPn3a7r33Xqtfv77Hr6CTJ0+2Z555xszS/tqeW6TXm99v69/+9jdr06aNxzH6GzdutGrVqqU59Ci3+frrr93/PX/+fLvlllvs4Ycftrp169qiRYvs4sWLZvbb6+Py5cs2ceJECwwMtLfeess6deqU5vC33GTGjBk2f/58i4+Pt/nz51vjxo2tXbt2dtddd9lHH31kiYmJVqdOHWvZsmWefP2Y/XbOT2RkpL300ktm9tsvwM8884xVqlTJFixYYGZXz3cNDAy0JUuWWEpKivt96tVXX7WaNWv6ZgNywN69e+3uu+92zz6X2p/IyEhzuVzuWSHff/99GzRokA0ZMsQefPBBGzJkiN19993uvVW55VIevz864D//+Y97L4rZ1fOmGjVqZJ999pnHDxLpSUpKsv/3//6fTZ069brU6guXL1+2YcOG2bhx4+z06dM2bNgwa9q0qZn99trZuHGj3XvvvR6XWTD7LTA88sgj7j3j06dPt1tvvTVX74m61k8//WSzZ8+2uLg4e/XVV+322293n3OX+p5z7tw5e/nlly1fvnweh/Bt27bN6tatm2v2+v4ZhKk8JPUPpUuXLta6dWtr1aqVbdmyxddl5ahrP5i++uorCw0NtQkTJniM2bVrl5UpU8aaN29uW7ZssQMHDtgDDzyQqw87MvPem9R1H374oblcLhswYICdPn3a9u7daw0bNrRXX30114bM30tISLCRI0fa5s2bbd++fdalSxe7/fbb091LNX/+fAsICLCGDRt6/HqXG128eNFee+0169Onj3Xo0MF2795te/bssaioKLvtttvsrbfeshUrVuS514+TyW5iY2Pt+PHjFhYWZrVq1XJP/W1m9uKLL3rstbnR/X7Cm9jYWAsNDbWJEyea2W9f6GbMmGEul8vCwsLs4sWL9vbbb1uVKlVsxIgR7kOT3n33XXO5XFa9enWbMmWKx2QUN6JrJ7tJ/bHGzNw/Rnz33Xd21113WcuWLa1Vq1aWL18+Gzx4sJ08edLMrn7ZTT1v0+xqEPv73//uvtTAjW727NlWv359a9OmjT300EO2fPlyu3jxov3vf/+zlJQUd5g6ePCglSxZMt3Lu3z77bf22GOP2d69e+3xxx+3/PnzW7t27XLlkQQZnYOYGp7WrFljYWFh1rFjxzT3PXLkiDVt2tTuvvtu9w8+b7/9tnXo0CHd84bzGsJUHpKcnGy7du2yDz/8MM/MRONNYmKivfzyy1asWDH3NTlSf6navHmzvfDCC9ayZUurXbu2xyxaeUF6vUn1j3/8w+655x6rV6+e/f3vf89TvcnofITIyEirXbu2HThwwMyufjH85ptvLDw8PNfPYnStVatW2dNPP21bt271WN6oUSNr0KCBXbhwwX0ByNz8+vkzk91UrFjRPdvYwYMHrXLlynbffffZ6NGjrUWLFvb444/bDz/8kINbk/0y6k/q39dTTz1lpUuX9tiT8Oabb1r37t2tTp06tnDhQouNjfUIBbt377b77rvP2rRp4xE+b0RZmewm9TB9M7Nx48ZZiRIl3OeMrVmzxsqUKWN9+vSxpk2b2uOPP55mFtEb1eHDhz2258CBA3bp0qU04TkpKckOHz5skZGR7hB67SFsM2fOtNKlS9vNN99s1atXT/PelRtkdA5i6pESqT27cuWKvfHGG1ayZElbuXKlmZnHkTvx8fHWu3dva9SokdWvX98eeeSRNJO75FWEKeRpBw8etDJlylivXr3SXZ8bp4XPLG+9uXz5cp6cscfsty98ycnJ7g/lBQsW2M033+wOU2YZX0A0N0tKSnLv7b526u733nvPChYs6D7nLC4uLte/fv7sZDepe+q+//57W7p0qQ0aNMjjXI4bXXr9SXX69GmrVKmS3Xnnnfb6669bjx49rHPnzrZ9+3arU6eOx2FuqX+PJ06cyBWH9mVlsptrvwSnqlWrlj3yyCNmdnUv+scff2zDhg3LdechDh48OM25Y7//sSv1/WfVqlXWtWtXM7v6ubZx40Y7f/68mV09f7Fo0aK5+kevzJyDmPoa2r17tzVr1izNOeLXXmbgwoULuWbypOxy41+lDvgT7rjjDvXv31/vvvuuDh06pJUrV2rbtm3u9bfeeqsPq/Ot9HqzdetWSVcvBHnvvff6uELfSL2o47UX5C1XrpzKly+vkJAQ97iMLiCamwUGBqpWrVqSrl4MPDk5WdLV/lSuXFnx8fGSpJCQkFz/+lm1apUqVKigCRMmaMmSJfrss8/cFxv+y1/+ohdeeEGTJk3S119/7b5PkSJF1LRpU61du1ZBQUGSpDvvvFNt2rTRyJEj1aZNG59sy/WQXn+kqxdsLl68uFasWKHXX39dAQEBqlWrlqKjo1W9enXdf//92rJli/txUv8eS5UqpebNm/tkW7KTy+WSy+VS06ZN3ctSUlL01FNPpTtWunrR3cuXL0uS6tWrp127dsnMlD9/frVu3VpDhw5Vy5Ytc2YDckihQoU8Lvo9cuRI9e/fX4MHD9aOHTsk/fYevGbNGtWsWVOSlJCQoOjoaO3atUuS1L17d507d06PPvpoDm9Bzti3b582btyookWLSrr6Wmrfvr374ruzZs2S9NtrqUqVKnrsscd09OhRTZs2TZIUHx/v7qXL5dItt9yiypUr+2Br/BdhCnlaUFCQnn32WZUtW1ZVq1bV1KlTlT9/fl+X5RfS601wcLCvy/ILffr00bZt2xQcHKxDhw5p2LBhatasmUqVKuXr0vzCnDlztG/fPuXPn19HjhzRmDFj1KJFCxUvXtzXpeWY8PBw9e/fX40bN1arVq3Uv39/XblyRdLVL3m9evVSpUqV1L9/f+3bt899v0qVKqlatWqSJDPzSe05Ib3+JCUlucPRnXfeqS5dumjYsGHq2bOn+36FChVSnTp1JF39YpjbVKxYUYMGDVLBggUlSQsWLNBrr72m7du36+9//7sWL16s2NhY95ff1FBQoEABSdK5c+f09NNPu9fnVhcuXNAPP/ygTZs2KTIyUmfPnlW9evU0d+5cRUVFacWKFZKkK1eu6Mcff1T16tV17NgxTZ48WTNmzNDBgwclSaVLl/blZmS71B8aUt87SpUqpWPHjumHH36QJPcPXM2aNZMkTZs2TadPn1ZgYKD7x8HGjRurdevW+ve//61JkyZp8ODBOnXqVE5vyo3Fx3vGAJ9JPUH1+eeft9q1a9vMmTN9XZLfoDferV271kqVKmUdOnSwhg0belzQGVevW1KjRg17/PHHrXHjxnmyP0x2411m+mN29cT31NnCDhw4YG3btrWNGzfmWJ2+9EeT3fz4449WpUoV++CDD+zLL7+0Dh06WNu2be3w4cO+LTwHbN261fLly2cvvPCCTZkyxb18+/bt1rhxY6tevbqZXT18rWXLlvbiiy9aiRIl7K9//WuuuwzMnzkH8f7770/3833EiBHmcrmsVatWtn///hzYihsbYQp53rx585iNJgP0JmPLli2zd955h/5kYM2aNTZnzpxcOStWVjHZjXfeJrxZtGiRNW7c2MLDw61hw4Z5pj+Znexm9OjRdv/991u9evXso48+yskSfa59+/bmcrnc15VKDRUzZ860SpUq2c6dO23r1q3mcrmsZMmSNnnyZF+We11lxzmIqT+irlixwiIiInLdeXbXk8ssFx9HAACAHzh06JAaNGigli1basqUKWnWnzt3Lk+fo+mtPzNnzlR8fLyee+45H1XnGykpKQoICFBKSorMTIGBgVq4cKGefPJJbdu2TXfddZck6dixY7rjjjt8XG3O27Vrl2rWrKnmzZtr0qRJuu222yRJMTExaty4sbZt26bQ0FDNmTNHffv29XG111d0dLSOHj2qGjVq6OGHH9ann36qpk2bul9DBw8e1IYNG3T48GGVLFnSfehs3759dfjwYS1atMj9WOfPn3efY4XMIUwBAHCdJSUlafr06erbt6927dqlAwcOqHjx4rrvvvt8XZpfSK8/RYsWVVhYmMws158D9EcSEhIUHBysr7/+Wk899ZT++9//5rrzfZwYNWqU/vWvf2ngwIHq16+fJGnhwoVauXJlnjrP99ChQypdurSSk5MVFRWlw4cPa+vWre6JbDIyYsQIhYSEqF+/fkpOTs6TEydlByagAADgOmOyG+/S60/q7Jh5OUgx2Y13gwYN0lNPPaUJEyaoa9euatOmjebNm6devXrlmSAlSeXLl1dwcLBCQ0M1cOBAff/995o6dWqacUePHtWxY8ckSd9//72++eYb1atXT1LenIE2u7BnCgCA68iunp+sF198UV9//bWefvpp9ejRw9dl+Q36k7F169apQ4cOioiI0OnTp9W1a1d16tTJ12X5lZSUFO3du1e7d+9WYmKioqKifF2ST125ckUDBgzQ7NmztXv3bo9ZVBcvXqxp06YpPj5e+fLlU69evfTII4/4sNrcgTAFAEAOeP/999WuXbs89Yt5VtCf9H3yySf66aef1KlTJ3qDTOEcxJxFmAIAAAByCc5BzFmcMwUAAADkEpyDmLO8T/MBAAAA4IZw7TmIhQsX1qRJkzgH8TrjMD8AAAAgF+EcxJxDmAIAAAAABzhnCgAAAAAcIEwBAAAAgAOEKQAAAABwgDAFAAAAAA4QpgAAAADAAcIUAAAAADhAmAIAAAAABwhTAAAAAOBAkK8LAADAqXLlyik0NFT58+eXJP3yyy86ePCgypQpo6JFi0qSEhMTFRcXpw8++EAPPfSQoqOj1aZNG1+WDQDIJQhTAIAb2ooVK1SuXDlJ0po1a/TAAw9oxIgR6tq1qyTpyJEjatCggQoUKKCyZcuqcOHCvisWAJCrEKYAADesiIgIhYSEeB0TEhKiiIgIVa1aVdu3b8+hygAAeQHnTAEAbljR0dEqUaKE1zElSpRQ27Ztdc8998jlcmnYsGGSpH379umee+7RzTffrAYNGmjBggWKiIjQbbfdphYtWuj06dP6/vvv1bZtW1WsWFH33XefNm/enObxt2zZosaNG6t8+fIqX768mjZtqm+//fY6bC0AwN8QpgAAuV5kZGSagFO5cmV9++23qlWrlvbs2aMTJ05o7dq12rt3r/bu3asePXronXfe0UcffaQDBw6oQoUKioqKUnJysvsxYmJiVL9+fd1zzz06fPiwDh8+rGrVqql+/fr6/vvvc3grAQA5jTAFAMjzkpKS9Pzzz0uSChUqpObNm2v58uV67LHHFBQUJJfLpfbt2+vgwYM6fPiw+359+/ZVaGio/vGPf7iXjRgxQmamMWPG5Ph2AAByFmEKAJDnVahQQfny5XPfTp0J8O6773Yvu/XWWyVJJ0+elCTFxcVpw4YNqlWrlm666Sb3uNDQUN155536v//7v5woHQDgQ0xAAQDI8woUKOBx2+VypVkeEHD198fUw/wuXLig5ORkbdmyRffcc4/H/c+fP+9+DABA7kWYAgDAgSJFiiggIEARERFasmSJr8sBAPgAh/kBAOBAaGiowsPDtX37dqWkpHisW7p0qYYPH+6jygAAOYUwBQCAQ//85z918uRJ96QT0tUp11988UXdd999Pq4OAHC9uSz13R8AgBvYI488om+//VYHDx5UmTJlVKlSJa1evVqStGTJEg0fPlzbt29XiRIlVKVKFX300Ud68MEH3VOYV6xYUevXr9eTTz6ptWvX6qefflKNGjU0fvx4xcTEaPr06Tp48KDuvPNOde7cWUOGDJF0dXr0QYMGadeuXSpRooRCQ0PVr18/tW7d2me9AADkDMIUAAAAADjAYX4AAAAA4ABhCgAAAAAcIEwBAAAAgAOEKQAAAABwgDAFAAAAAA4QpgAAAADAAcIUAAAAADhAmAIAAAAABwhTAAAAAOAAYQoAAAAAHCBMAQAAAIADhCkAAAAAcIAwBQAAAAAO/H/w87XTOipK0AAAAABJRU5ErkJggg==\n", "text/plain": [ - "
" + "
" ] }, "metadata": {}, @@ -581,16 +581,16 @@ "\n", "# Compute the Brier Score and time-dependent concordance index for the test set to assess model performance\n", "results = dict()\n", - "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", - "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", + "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", + "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", "plot_performance_metrics(results, times)" ] }, { "cell_type": "markdown", - "id": "8dc206f6", + "id": "09ad9584", "metadata": {}, "source": [ "\n", @@ -599,7 +599,7 @@ }, { "cell_type": "markdown", - "id": "a05dab92", + "id": "83d13bf2", "metadata": {}, "source": [ "DCPH [2], [3] is an extension to the CPH model. DCPH involves modeling the proportional hazard ratios over the individuals with Deep Neural Networks allowing the ability to learn non linear hazard ratios.\n", @@ -613,7 +613,7 @@ }, { "cell_type": "markdown", - "id": "1f5123e4", + "id": "ee882560", "metadata": {}, "source": [ "\n", @@ -623,7 +623,7 @@ { "cell_type": "code", "execution_count": 7, - "id": "8db562b5", + "id": "0eb815b5", "metadata": {}, "outputs": [ { @@ -667,7 +667,7 @@ "\n", " # Obtain survival probabilities for validation set and compute the Integrated Brier Score \n", " predictions_val = model.predict_survival(x_val, times)\n", - " metric_val = survival_regression_metric('ibs', y_tr, y_val, predictions_val, times)\n", + " metric_val = survival_regression_metric('ibs', y_tr, predictions_val, times, y_val)\n", " models.append([metric_val, model])\n", " \n", "# Select the best model based on the mean metric value computed for the validation set\n", @@ -678,7 +678,7 @@ }, { "cell_type": "markdown", - "id": "65df1701", + "id": "8983b804", "metadata": {}, "source": [ "\n", @@ -687,7 +687,7 @@ }, { "cell_type": "markdown", - "id": "7332567b", + "id": "28aad20c", "metadata": {}, "source": [ "Compute the Brier Score and time-dependent concordance index for the test set. See notebook introduction for more details." @@ -696,7 +696,7 @@ { "cell_type": "code", "execution_count": 8, - "id": "940a4b53", + "id": "dd8caa11", "metadata": {}, "outputs": [ { @@ -718,10 +718,10 @@ "\n", "# Compute the Brier Score and time-dependent concordance index for the test set to assess model performance\n", "results = dict()\n", - "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", - "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", + "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", + "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", "plot_performance_metrics(results, times)" ] }, @@ -732,7 +732,7 @@ } }, "cell_type": "markdown", - "id": "0adde949", + "id": "b875cc6c", "metadata": {}, "source": [ "\n", @@ -753,7 +753,7 @@ }, { "cell_type": "markdown", - "id": "00577b19", + "id": "68ee298a", "metadata": {}, "source": [ "\n", @@ -763,7 +763,7 @@ { "cell_type": "code", "execution_count": 9, - "id": "c3f56926", + "id": "2f1e347f", "metadata": {}, "outputs": [ { @@ -823,7 +823,7 @@ "\n", " # Obtain survival probabilities for validation set and compute the Integrated Brier Score \n", " predictions_val = model.predict_survival(x_val, times)\n", - " metric_val = survival_regression_metric('ibs', y_tr, y_val, predictions_val, times)\n", + " metric_val = survival_regression_metric('ibs', y_tr, predictions_val, times, y_val)\n", " models.append([metric_val, model])\n", " \n", "# Select the best model based on the mean metric value computed for the validation set\n", @@ -834,7 +834,7 @@ }, { "cell_type": "markdown", - "id": "d07820c7", + "id": "81c45ead", "metadata": {}, "source": [ "\n", @@ -843,7 +843,7 @@ }, { "cell_type": "markdown", - "id": "6a1a8b6d", + "id": "ad13ad21", "metadata": {}, "source": [ "Compute the Brier Score and time-dependent concordance index for the test set. See notebook introduction for more details." @@ -852,7 +852,7 @@ { "cell_type": "code", "execution_count": 10, - "id": "53a5177f", + "id": "d0d5b400", "metadata": {}, "outputs": [ { @@ -874,16 +874,16 @@ "\n", "# Compute the Brier Score and time-dependent concordance index for the test set to assess model performance\n", "results = dict()\n", - "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", - "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", + "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", + "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", "plot_performance_metrics(results, times)" ] }, { "cell_type": "markdown", - "id": "6ca3f1aa", + "id": "8185b74e", "metadata": {}, "source": [ "\n", @@ -897,7 +897,7 @@ } }, "cell_type": "markdown", - "id": "10a8218a", + "id": "82daa2f4", "metadata": {}, "source": [ "DCM [2] generalizes the proportional hazards assumption via a mixture model, by assuming that there are latent groups and within each, the proportional hazards assumption holds. DCM allows the hazard ratio in each latent group, as well as the latent group membership, to be flexibly modeled by a deep neural network.\n", @@ -915,7 +915,7 @@ }, { "cell_type": "markdown", - "id": "c499b948", + "id": "03951373", "metadata": {}, "source": [ "\n", @@ -925,7 +925,7 @@ { "cell_type": "code", "execution_count": 11, - "id": "841bf58c", + "id": "065c68c6", "metadata": {}, "outputs": [ { @@ -1013,7 +1013,7 @@ "\n", " # Obtain survival probabilities for validation set and compute the Integrated Brier Score \n", " predictions_val = model.predict_survival(x_val, times)\n", - " metric_val = survival_regression_metric('ibs', y_tr, y_val, predictions_val, times)\n", + " metric_val = survival_regression_metric('ibs', y_tr, predictions_val, times, y_val)\n", " models.append([metric_val, model])\n", " \n", "# Select the best model based on the mean metric value computed for the validation set\n", @@ -1024,7 +1024,7 @@ }, { "cell_type": "markdown", - "id": "9bf28113", + "id": "4794d4f9", "metadata": {}, "source": [ "\n", @@ -1033,7 +1033,7 @@ }, { "cell_type": "markdown", - "id": "0ab4b8cf", + "id": "b6248233", "metadata": {}, "source": [ "Compute the Brier Score and time-dependent concordance index for the test set. See notebook introduction for more details." @@ -1042,7 +1042,7 @@ { "cell_type": "code", "execution_count": 12, - "id": "a2acb333", + "id": "2ee57c19", "metadata": {}, "outputs": [ { @@ -1064,16 +1064,16 @@ "\n", "# Compute the Brier Score and time-dependent concordance index for the test set to assess model performance\n", "results = dict()\n", - "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", - "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", + "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", + "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", "plot_performance_metrics(results, times)" ] }, { "cell_type": "markdown", - "id": "bac6b75f", + "id": "006724a1", "metadata": {}, "source": [ "\n", @@ -1090,7 +1090,7 @@ }, { "cell_type": "markdown", - "id": "7f054819", + "id": "c26416ad", "metadata": {}, "source": [ "\n", @@ -1100,7 +1100,7 @@ { "cell_type": "code", "execution_count": 13, - "id": "7f9358ae", + "id": "8e039745", "metadata": {}, "outputs": [], "source": [ @@ -1129,7 +1129,7 @@ "\n", " # Obtain survival probabilities for validation set and compute the Integrated Brier Score \n", " predictions_val = model.predict_survival(x_val, times)\n", - " metric_val = survival_regression_metric('ibs', y_tr, y_val, predictions_val, times)\n", + " metric_val = survival_regression_metric('ibs', y_tr, predictions_val, times, y_vals)\n", " models.append([metric_val, model])\n", " \n", "# Select the best model based on the mean metric value computed for the validation set\n", @@ -1140,7 +1140,7 @@ }, { "cell_type": "markdown", - "id": "1bd45b34", + "id": "b0680104", "metadata": {}, "source": [ "\n", @@ -1149,7 +1149,7 @@ }, { "cell_type": "markdown", - "id": "4b44cd85", + "id": "5135b083", "metadata": {}, "source": [ "Compute the Brier Score and time-dependent concordance index for the test set. See notebook introduction for more details." @@ -1158,7 +1158,7 @@ { "cell_type": "code", "execution_count": 14, - "id": "e7710fb4", + "id": "27fa6905", "metadata": {}, "outputs": [ { @@ -1180,17 +1180,17 @@ "\n", "# Compute the Brier Score and time-dependent concordance index for the test set to assess model performance\n", "results = dict()\n", - "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", - "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, outcomes_test=y_te, \n", - " predictions=predictions_te, times=times)\n", + "results['Brier Score'] = survival_regression_metric('brs', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", + "results['Concordance Index'] = survival_regression_metric('ctd', outcomes_train=y_tr, predictions=predictions_te, \n", + " times=times, outcomes_test=y_te)\n", "plot_performance_metrics(results, times)" ] }, { "cell_type": "code", "execution_count": null, - "id": "e71903bf", + "id": "7cfaac62", "metadata": {}, "outputs": [], "source": []