docstr: Compute permutation feature importance for as many features as possible under time_budget. Ensure each returned feature importance
  scores are computed from at least n_sample datapoints.
docstr_tok: ["Compute","permutation","feature","importance","for","as","many","features","as","possible","under","time_budget",".","Ensure","each","returned","feature","importance","scores","are","computed","from","at","least","n_sample","datapoints","."]
code: def compute_next_candidate_round(
      self,
      X: pd.DataFrame,
      y: pd.Series,
      model: AbstractModel,
      time_budget: float,
      features: List[str],
      n_subsample: int,
      min_fi_samples: int,
      max_fi_samples: int,
      prune_ratio: float,
      prune_threshold: float,
      prev_importance_df: pd.DataFrame = None,
      prioritized: Set[str] = set(),
      using_prev_fit_fi: Set[str] = set(),
      weighted: bool = True,
  ) -> Tuple[List[str], pd.DataFrame, float]:
      """
      Compute permutation feature importance for as many features as possible under time_budget. Ensure each returned feature importance
      scores are computed from at least n_sample datapoints.
      """
      # determine how many subsamples and shuffles to use for feature importance calculation
      time_start = time.time()
      n_features = len(features)
      n_subsample = min(n_subsample, len(X))
      n_total_sample = max(min_fi_samples, min(max_fi_samples, len(X)))
      n_shuffle = min(
          np.ceil(n_total_sample / n_subsample).astype(int),
          self.max_n_shuffle,
      )
      single_feature_fi_time = self.compute_expected_fi_time_single(
          X_fi=X,
          model_predict_time=self.model_predict_time,
          n_subsample=n_subsample,
          n_total_sample=n_total_sample,
      )
      noise_threshold = len(prioritized) > 0
      features = sort_features_by_priority(
          features=features,
          prev_importance_df=prev_importance_df,
          using_prev_fit_fi=using_prev_fit_fi,
      )
      if noise_threshold:
          features = list(prioritized) + [
              feature
              for feature in features
              if feature not in prioritized
          ]
  
      # if we do not have enough time to evaluate feature importance for all features, do so only for some (first n_evaluated_features elements of features)
      n_evaluated_features = max(
          [
              i
              for i in range(0, n_features + 1)
              if i * single_feature_fi_time + self.model_predict_time
              <= time_budget
          ]
      )
      if n_evaluated_features == 0:
          prune_time = time.time() - time_start
          return (
              features,
              unevaluated_fi_df_template(features),
              prune_time,
          )
      evaluated_features = features[:n_evaluated_features]
      unevaluated_features = features[n_evaluated_features:]
      time_budget_fi = time_budget - (time.time() - time_start)
      logger.log(
          15,
          f"\tComputing feature importance for {n_evaluated_features}/{n_features} features with {n_shuffle} shuffles.",
      )
      fi_kwargs = {
          "X": X,
          "y": y,
          "num_shuffle_sets": n_shuffle,
          "subsample_size": n_subsample,
          "features": evaluated_features,
          "time_limit": time_budget_fi,
          "silent": True,
          "random_state": self.rng.integers(low=0, high=1e5),
      }
      fi_kwargs.update({"is_oof": True} if self.is_bagged else {})
      # FIXME: Right now the upper bound on the number of features we evaluate feature importance at once is determined by our expected feature
      # importance computation time. While this estimate is relatively accurate on most datasets, on some high dimensional datasets it underestimates
      # the time needed to evaluate n_shuffles of permutations and ends up only evaluating a few shuffles. Consider making a feature importance
      # method that parallelizes across individual shuffles instead.
      evaluated_df = model.compute_feature_importance(**fi_kwargs)
      if self.is_bagged:
          # If the bagged model includes 5 models and we evaluate a single permutation feature importance shuffle, the above method returns n=5 instead of 1.
          evaluated_df["n"] = (
              evaluated_df["n"] // len(model.models)
          ).clip(lower=1)
  
      # if we could not compute feature importance for all features and previous feature importance estimates exist, use them
      importance_df = pd.concat(
          [
              evaluated_df,
              unevaluated_fi_df_template(unevaluated_features),
          ]
      )
      importance_df = merge_importance_dfs(
          prev_importance_df, importance_df, using_prev_fit_fi
      )
  
      # if using noise threshold, threshold is the mean of noise column importance score
      if noise_threshold:
          noise_rows = importance_df[
              importance_df.index.isin(prioritized)
          ]
          importance_df = importance_df.drop(prioritized)
          prune_threshold = noise_rows["importance"].mean()
  
      # use importance_df to generate next candidate features
      candidate_features = self.compute_next_candidate_given_fi(
          importance_df=importance_df,
          prune_threshold=prune_threshold,
          prune_ratio=prune_ratio,
          weighted=weighted,
      )
  
      # if noise columns exist, they should never be removed
      if noise_threshold:
          candidate_features = candidate_features + list(prioritized)
          importance_df = pd.concat([importance_df, noise_rows])
  
      feature_selection_time = time.time() - time_start
      return (
          candidate_features,
          importance_df.sort_values(by="importance", axis=0),
          feature_selection_time,
      )
  
imports: import logging
  import time
  import traceback
  import uuid
  from copy import deepcopy
  from typing import List, Set, Tuple, Union
  import numpy
  import pandas
  from autogluon.common.features.types import R_FLOAT
  from ..models.abstract.abstract_model import AbstractModel
  from ..models.ensemble.bagged_ensemble_model import BaggedEnsembleModel
  from ..utils.exceptions import TimeLimitExceeded
  from ..utils.utils import generate_train_test_split, unevaluated_fi_df_template
label_desc: Use image node_id 4 for calling the FeatureSelector obj's underlying member method code with example usage: obj.compute_next_candidate_round(X, y, model, time_budget, features, n_subsample, min_fi_samples, max_fi_samples, prune_ratio, prune_threshold, prev_importance_df, prioritized, using_prev_fit_fi, weighted) and returns: candidate_features,  importance_df,  feature_selection_time, features,  unevaluated_fi_df_template,  prune_time