docstr: This function ensures that we only vary features_to_vary when generating counterfactuals
docstr_tok: ["This","function","ensures","that","we","only","vary","features_to_vary","when","generating","counterfactuals"]
code: def vary_valid(
      self,
      KD_query_instance,
      total_CFs,
      features_to_vary,
      permitted_range,
      query_instance,
      sparsity_weight,
  ):
      """This function ensures that we only vary features_to_vary when generating counterfactuals"""
  
      # TODO: this should be a user-specified parameter
      num_queries = min(
          len(self.dataset_with_predictions), total_CFs * 10
      )
      cfs = []
  
      if self.KD_tree is not None and num_queries > 0:
          KD_tree_output = self.KD_tree.query(
              KD_query_instance, num_queries
          )
          distances = KD_tree_output[0][0]
          indices = KD_tree_output[1][0]
  
          cfs = self.dataset_with_predictions.iloc[indices].copy()
          cfs["distance"] = distances
          cfs = self.do_sparsity_check(
              cfs, query_instance, sparsity_weight
          )
          cfs = cfs.drop(self.data_interface.outcome_name, axis=1)
  
      self.final_cfs = pd.DataFrame()
      final_indices = []
      cfs_preds = []
      total_cfs_found = 0
  
      # Iterating through the closest points from the KD tree and checking if any of these are valid
      if self.KD_tree is not None and total_CFs > 0:
          for i in range(len(cfs)):
              if total_cfs_found == total_CFs:
                  break
              valid_cf_found = True
              for feature in self.data_interface.feature_names:
                  if (
                      feature not in features_to_vary
                      and cfs[feature].iat[i]
                      != query_instance[feature].values[0]
                  ):
                      valid_cf_found = False
                      break
                  if (
                      feature
                      in self.data_interface.continuous_feature_names
                  ):
                      if (
                          not self.feature_range[feature][0]
                          <= cfs[feature].iat[i]
                          <= self.feature_range[feature][1]
                      ):
                          valid_cf_found = False
                          break
                  else:
                      if (
                          not cfs[feature].iat[i]
                          in self.feature_range[feature]
                      ):
                          valid_cf_found = False
                          break
  
              if valid_cf_found:
                  if not self.duplicates(cfs, final_indices.copy(), i):
                      total_cfs_found += 1
                      final_indices.append(i)
      if total_cfs_found > 0:
          self.final_cfs = cfs.iloc[final_indices]
          self.final_cfs = self.final_cfs.drop(
              [self.predicted_outcome_name], axis=1
          )
          # Finding the predicted outcome for each cf
          for i in range(total_cfs_found):
              cfs_preds.append(
                  self.dataset_with_predictions.iloc[final_indices[i]][
                      self.predicted_outcome_name
                  ]
              )
  
      return self.final_cfs[:total_CFs], cfs_preds
  
file_path: DiCE/dice_ml/explainer_interfaces/dice_KD.py
imports: import copy
  import timeit
  import numpy
  import pandas
  from dice_ml import diverse_counterfactuals
  from dice_ml.constants import ModelTypes
  from dice_ml.explainer_interfaces.explainer_base import ExplainerBase
label_desc: Use image node_id 6 for calling the DiceKD obj's underlying member method code with example usage: obj.vary_valid(KD_query_instance, total_CFs, features_to_vary, permitted_range, query_instance, sparsity_weight) and returns: self,  cfs_preds