docs: def augmented_tf_idf(attribute_matrix):
      """
      Performs augmented TF-IDF normalization on a bag-of-words vector representation of data.
  
      Augmented TF-IDF introduced in: Manning, C. D., Raghavan, P., & Schütze, H. (2008).
                                      Introduction to information retrieval (Vol. 1, p. 6).
                                      Cambridge: Cambridge university press.
  
      Input:  - attribute_matrix: A bag-of-words vector representation in SciPy sparse matrix format.
  
      Output: - attribute_matrix: The same matrix after augmented tf-idf normalization.
      """
      number_of_documents = attribute_matrix.shape[0]
  
      max_term_frequencies = np.ones(number_of_documents, dtype=np.float64)
      idf_array = np.ones(attribute_matrix.shape[1], dtype=np.float64)
  
      # Calculate inverse document frequency
      attribute_matrix = attribute_matrix.tocsc()
      for j in range(attribute_matrix.shape[1]):
          document_frequency = attribute_matrix.getcol(j).data.size
          if document_frequency > 1:
              idf_array[j] = np.log(number_of_documents/document_frequency)
  
      # Calculate maximum term frequencies for a user
      attribute_matrix = attribute_matrix.tocsr()
      for i in range(attribute_matrix.shape[0]):
          max_term_frequency = attribute_matrix.getrow(i).data
          if max_term_frequency.size > 0:
              max_term_frequency = max_term_frequency.max()
              if max_term_frequency > 0.0:
                  max_term_frequencies[i] = max_term_frequency
  
      # Do augmented tf-idf normalization
      attribute_matrix = attribute_matrix.tocoo()
      attribute_matrix.data = 0.5 + np.divide(0.5*attribute_matrix.data, np.multiply((max_term_frequencies[attribute_matrix.row]), (idf_array[attribute_matrix.col])))
      attribute_matrix = attribute_matrix.tocsr()
  
      return attribute_matrix