code: def _transform(self, df: DataFrame) -> DataFrame:
      stats_method = self._make_stats_method()
      input_col = self.input_col
      output_col = self.output_col
  
      if self.is_partitioned():
          partition_key = self.partition_key
          per_group_stats = self.per_group_stats
  
          with_stats_df = df.join(
              per_group_stats, partition_key, how="left"
          )
      else:
          with_stats_df = df
  
      return with_stats_df.withColumn(
          output_col, stats_method(f.col(input_col))
      )
  
imports: from abc import ABC, abstractmethod
  from typing import Callable, Dict, List, Optional, Union
  from synapse.ml.cyber.utils.spark_utils import ExplainBuilder, HasSetInputCol, HasSetOutputCol
  from pyspark.ml import Estimator, Transformer
  from pyspark.ml.param.shared import HasInputCol, HasOutputCol, Param, Params
  from pyspark.sql import DataFrame, functions, types
label_desc: Use image node_id 8 for calling the PerPartitionScalarScalerModel obj's underlying member method code with example usage: obj._transform(df) and returns: with_stats_df