code: def proportional_hazard_test(
      fitted_cox_model,
      training_df,
      time_transform="rank",
      precomputed_residuals=None,
      **kwargs,
  ) -> StatisticalResult:
      """
      Test whether any variable in a Cox model breaks the proportional hazard assumption. This method uses an approximation
      that R's ``survival`` use to use, but changed it in late 2019, hence there will be differences here between lifelines and R.
  
      Parameters
      ----------
      fitted_cox_model: CoxPHFitter
          the fitted Cox model, fitted with `training_df`, you wish to test. Currently only the CoxPHFitter is supported,
          but later CoxTimeVaryingFitter, too.
      training_df: DataFrame
          the DataFrame used in the call to the Cox model's ``fit``. Optional if providing ``precomputed_residuals``
      time_transform: vectorized function, list, or string, optional (default='rank')
          {'all', 'km', 'rank', 'identity', 'log'}
          One of the strings above, a list of strings, or a function to transform the time (must accept (time, durations, weights) however). 'all' will present all the transforms.
      precomputed_residuals: DataFrame, optional
          specify the scaled Schoenfeld residuals, if already computed.
      kwargs:
          additional parameters to add to the StatisticalResult
  
      Notes
      ------
      R uses the default `km`, we use `rank`, as this performs well versus other transforms. See
      http://eprints.lse.ac.uk/84988/1/06_ParkHendry2015-ReassessingSchoenfeldTests_Final.pdf
  
      References
      -----------
       - http://eprints.lse.ac.uk/84988/1/06_ParkHendry2015-ReassessingSchoenfeldTests_Final.pdf
       - "Extending the Cox Model"
       - https://github.com/therneau/survival/commit/5da455de4f16fbed7f867b1fc5b15f2157a132cd#diff-c784cc3eeb38f0a6227988a30f9c0730R36
  
      """
      if "transform" in kwargs:
          warnings.warn(
              "Found 'transform' keyword being set. Did you mean to set 'time_transform' instead?",
              UserWarning,
          )
  
      events, durations, weights = (
          fitted_cox_model.event_observed,
          fitted_cox_model.durations,
          fitted_cox_model.weights,
      )
      n_deaths = events.sum()
  
      if precomputed_residuals is None:
          scaled_resids = fitted_cox_model.compute_residuals(
              training_df, kind="scaled_schoenfeld"
          )
      else:
          scaled_resids = precomputed_residuals
  
      def compute_statistic(times, resids, n_deaths):
          demeaned_times = times - times.mean()
          T = (demeaned_times.values[:, None] * resids.values).sum(
              0
          ) ** 2 / (
              n_deaths
              * (fitted_cox_model.standard_errors_**2)
              * (demeaned_times**2).sum()
          )
          return T
  
      if time_transform == "all":
          time_transform = list(
              TimeTransformers.TIME_TRANSFOMERS.keys()
          )
  
      if isinstance(time_transform, list):
          result = StatisticalResult([], [], [])
  
          # yuck
          for transform_name, transform in (
              (_, TimeTransformers().get(_)) for _ in time_transform
          ):
              times = transform(durations, events, weights)[
                  events.values
              ]
              T = compute_statistic(times, scaled_resids, n_deaths)
              p_values = utils._to_1d_array(
                  [_chisq_test_p_value(t, 1) for t in T]
              )
              result += StatisticalResult(
                  p_values,
                  T,
                  name=[
                      (c, transform_name)
                      for c in fitted_cox_model.params_.index
                  ],
                  test_name="proportional_hazard_test",
                  null_distribution="chi squared",
                  degrees_of_freedom=1,
                  model=str(fitted_cox_model),
                  **kwargs,
              )
  
      else:
          time_transformer = TimeTransformers().get(time_transform)
          assert callable(
              time_transformer
          ), "time_transform must be a callable function, or a string: {'rank', 'km', 'identity', 'log'}."
  
          times = time_transformer(durations, events, weights)[
              events.values
          ]
  
          T = compute_statistic(times, scaled_resids, n_deaths)
  
          p_values = utils._to_1d_array(
              [_chisq_test_p_value(t, 1) for t in T]
          )
          result = StatisticalResult(
              p_values,
              T,
              name=fitted_cox_model.params_.index.tolist(),
              test_name="proportional_hazard_test",
              time_transform=time_transform,
              null_distribution="chi squared",
              degrees_of_freedom=1,
              model=str(fitted_cox_model),
              **kwargs,
          )
      return result
  
imports: from itertools import combinations
  import warnings
  import numpy
  from scipy import stats
  import pandas
  from lifelines import utils
  from lifelines.utils import group_survival_table_from_events, string_rjustify, format_p_value, format_floats, interpolate_at_times_and_return_pandas, _expected_value_of_survival_up_to_t, _expected_value_of_survival_squared_up_to_t
  from lifelines import KaplanMeierFitter
label_desc: Use image node_id 9 for calling a global function with example usage: proportional_hazard_test(fitted_cox_model, training_df, time_transform, precomputed_residuals) and returns: result, T