code: def hash_mdp_id_and_subsample(
      df, sample_range: Optional[Tuple[float, float]] = None
  ):
      """Since mdp_id is a string but Pytorch Tensors do not store strings,
      we hash them with crc32, which is treated as a cryptographic hash
      (with range [0, MAX_UINT32-1]). We also perform an optional subsampling
      based on this hash value.
      NOTE: we're assuming no collisions in this hash! Otherwise, two mdp_ids
      can be indistinguishable after the hash.
      TODO: change this to a deterministic subsample.
      """
      if sample_range:
          assert (
              0.0 <= sample_range[0]
              and sample_range[0] <= sample_range[1]
              and sample_range[1] <= 100.0
          ), f"{sample_range} is invalid."
  
      df = df.withColumn("mdp_id", crc32(col("mdp_id")))
      if sample_range:
          lower_bound = sample_range[0] / 100.0 * MAX_UINT32
          upper_bound = sample_range[1] / 100.0 * MAX_UINT32
          # pyre-fixme[58]: `<=` is not supported for operand types `float` and `Column`.
          # pyre-fixme[58]: `<=` is not supported for operand types `Column` and `float`.
          df = df.filter(
              (lower_bound <= col("mdp_id"))
              & (col("mdp_id") <= upper_bound)
          )
      return df
  
imports: import logging
  from typing import List, Optional, Tuple
  from pyspark.sql.functions import col, crc32, explode, map_keys, udf
  from pyspark.sql.types import ArrayType, BooleanType, FloatType, LongType, MapType, StructField, StructType
  from reagent.data.data_fetcher import DataFetcher
  from reagent.data.spark_utils import get_spark_session, get_table_url
  from reagent.workflow.types import Dataset, TableSpec
label_desc: Use image node_id 4 for calling a global function with example usage: hash_mdp_id_and_subsample(df, sample_range) and returns: df