docstr: Recompute the hazard ratio at different follow-up times (lifelines handles accounting for updated censoring and updated durations).
  This is useful because we need to remember that the hazard ratio is actually a weighted-average of period-specific hazard ratios.
  
  Parameters
  ----------
  
  training_df: pd.DataFrame
      The same dataframe used to train the model
  followup_times: Iterable
      a list/array of follow-up times to recompute the hazard ratio at.
docstr_tok: ["Recompute","the","hazard","ratio","at","different","follow-up","times","(","lifelines","handles","accounting","for","updated","censoring","and","updated","durations",")",".","This","is","useful","because","we","need","to","remember","that","the","hazard","ratio","is","actually","a","weighted-average","of","period-specific","hazard","ratios",".","Parameters","--","--","--","--","--","training_df",":","pd.DataFrame","The","same","dataframe","used","to","train","the","model","followup_times",":","Iterable","a","list\/array","of","follow-up","times","to","recompute","the","hazard","ratio","at","."]
code: def compute_followup_hazard_ratios(
      self, training_df: DataFrame, followup_times: Iterable
  ) -> DataFrame:
      """
      Recompute the hazard ratio at different follow-up times (lifelines handles accounting for updated censoring and updated durations).
      This is useful because we need to remember that the hazard ratio is actually a weighted-average of period-specific hazard ratios.
  
      Parameters
      ----------
  
      training_df: pd.DataFrame
          The same dataframe used to train the model
      followup_times: Iterable
          a list/array of follow-up times to recompute the hazard ratio at.
  
  
      """
      results = {}
      for t in sorted(followup_times):
          assert (
              t <= training_df[self.duration_col].max()
          ), "all follow-up times must be less than max observed duration"
          df = training_df.copy()
          # if we "rollback" the df to time t, who is dead and who is censored
          df[self.event_col] = (df[self.duration_col] <= t) & df[
              self.event_col
          ]
          df[self.duration_col] = np.minimum(df[self.duration_col], t)
  
          model = self.__class__(
              penalizer=self.penalizer, l1_ratio=self.l1_ratio
          ).fit(
              df,
              self.duration_col,
              self.event_col,
              weights_col=self.weights_col,
              entry_col=self.entry_col,
          )
          results[t] = model.hazard_ratios_
      return DataFrame(results).T
  
file_path: lifelines/lifelines/fitters/mixins.py
imports: from typing import List, Optional, Dict, Any, Iterable
  from textwrap import dedent, fill
  from autograd import numpy
  import numpy
  from pandas import DataFrame, Series
  from lifelines.exceptions import ProportionalHazardAssumptionError
  from lifelines.statistics import proportional_hazard_test, TimeTransformers
  from lifelines.utils import format_p_value
  from lifelines.utils.lowess import lowess
label_desc: Use image node_id 3 for calling the ProportionalHazardMixin obj's underlying member method code with example usage: obj.compute_followup_hazard_ratios(training_df, followup_times) and returns: DataFrame