docstr: Calculates all metrics related to perturbed words in an attack.
  
  Args:
      results (``AttackResult`` objects):
          Attack results for each instance in dataset
code: def calculate(self, results):
      """Calculates all metrics related to perturbed words in an attack.
  
      Args:
          results (``AttackResult`` objects):
              Attack results for each instance in dataset
      """
  
      self.results = results
      self.total_attacks = len(self.results)
      self.all_num_words = np.zeros(len(self.results))
      self.perturbed_word_percentages = np.zeros(len(self.results))
      self.num_words_changed_until_success = np.zeros(2**16)
      self.max_words_changed = 0
  
      for i, result in enumerate(self.results):
          self.all_num_words[i] = len(
              result.original_result.attacked_text.words
          )
  
          if isinstance(result, FailedAttackResult) or isinstance(
              result, SkippedAttackResult
          ):
              continue
  
          num_words_changed = len(
              result.original_result.attacked_text.all_words_diff(
                  result.perturbed_result.attacked_text
              )
          )
          self.num_words_changed_until_success[
              num_words_changed - 1
          ] += 1
          self.max_words_changed = max(
              self.max_words_changed or num_words_changed,
              num_words_changed,
          )
          if len(result.original_result.attacked_text.words) > 0:
              perturbed_word_percentage = (
                  num_words_changed
                  * 100.0
                  / len(result.original_result.attacked_text.words)
              )
          else:
              perturbed_word_percentage = 0
  
          self.perturbed_word_percentages[i] = perturbed_word_percentage
  
      self.all_metrics[
          "avg_word_perturbed"
      ] = self.avg_number_word_perturbed_num()
      self.all_metrics[
          "avg_word_perturbed_perc"
      ] = self.avg_perturbation_perc()
      self.all_metrics["max_words_changed"] = self.max_words_changed
      self.all_metrics[
          "num_words_changed_until_success"
      ] = self.num_words_changed_until_success
  
      return self.all_metrics
  
file_path: TextAttack/textattack/metrics/attack_metrics/words_perturbed.py
imports: import numpy
  from textattack.attack_results import FailedAttackResult, SkippedAttackResult
  from textattack.metrics import Metric
label_desc: Use image node_id 2 for calling the WordsPerturbed obj's underlying member method code with example usage: obj.calculate(results) and returns: self