docstr: Keep features whose importance scores are above threshold or have not yet had a chance to be calculated,
  as well as some features whose importance scores are below threshold if more than prune_ratio * num features
  features are below threshold. In the latter case, randomly sample without replacement from features whose
  importance scores are below threshold until removal candidate configuration that has not yet been tried
  is encountered. Give higher probability to features whose scores are lower than others when sampling.
docstr_tok: ["Keep","features","whose","importance","scores","are","above","threshold","or","have","not","yet","had","a","chance","to","be","calculated",",","as","well","as","some","features","whose","importance","scores","are","below","threshold","if","more","than","prune_ratio","*","num","features","features","are","below","threshold",".","In","the","latter","case",",","randomly","sample","without","replacement","from","features","whose","importance","scores","are","below","threshold","until","removal","candidate","configuration","that","has","not","yet","been","tried","is","encountered",".","Give","higher","probability","to","features","whose","scores","are","lower","than","others","when","sampling","."]
code: def compute_next_candidate_given_fi(
      self,
      importance_df: pd.DataFrame,
      prune_threshold: float,
      prune_ratio: float,
      weighted: bool,
  ) -> List[str]:
      """
      Keep features whose importance scores are above threshold or have not yet had a chance to be calculated,
      as well as some features whose importance scores are below threshold if more than prune_ratio * num features
      features are below threshold. In the latter case, randomly sample without replacement from features whose
      importance scores are below threshold until removal candidate configuration that has not yet been tried
      is encountered. Give higher probability to features whose scores are lower than others when sampling.
      """
      n_remove = max(1, int(prune_ratio * len(importance_df)))
      above_threshold_rows = importance_df[
          (importance_df["importance"] > prune_threshold)
          | (importance_df["importance"].isna())
      ]
      below_threshold_rows = importance_df[
          importance_df["importance"] <= prune_threshold
      ].sort_values(by="importance", axis=0, ascending=True)
      logger.log(
          15,
          f"\tNumber of identified features below prune threshold {round(prune_threshold, 4)}: {len(below_threshold_rows)}/{len(importance_df)}",
      )
      if len(below_threshold_rows) <= n_remove:
          acceptance_candidates = above_threshold_rows.index.tolist()
          self.attempted_removals.add(tuple(below_threshold_rows.index))
          return acceptance_candidates
  
      # Try removing features with lowest importance first
      removal_candidate_rows = below_threshold_rows[:n_remove]
      removal_candidates = tuple(removal_candidate_rows.index)
      if removal_candidates not in self.attempted_removals:
          acceptance_candidates = importance_df[
              ~importance_df.index.isin(removal_candidates)
          ].index.tolist()
          self.attempted_removals.add(removal_candidates)
          return acceptance_candidates
  
      sample_weights = (
          [1 / i for i in range(1, len(below_threshold_rows) + 1)]
          if weighted
          else None
      )
      for _ in range(50):
          random_state = self.rng.integers(low=0, high=1e5)
          removal_candidate_rows = below_threshold_rows.sample(
              n=n_remove,
              random_state=random_state,
              replace=False,
              weights=sample_weights,
          )
          removal_candidates = tuple(removal_candidate_rows.index)
          if removal_candidates not in self.attempted_removals:
              acceptance_candidates = importance_df[
                  ~importance_df.index.isin(removal_candidates)
              ].index.tolist()
              self.attempted_removals.add(removal_candidates)
              return acceptance_candidates
      return importance_df.index.tolist()
  
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 5 for calling the FeatureSelector obj's underlying member method code with example usage: obj.compute_next_candidate_given_fi(importance_df, prune_threshold, prune_ratio, weighted) and returns: importance_df, acceptance_candidates, acceptance_candidates, acceptance_candidates