code: def state_and_metrics_sparse2dense(
      df,
      states: List[int],
      metrics: List[str],
      multi_steps: Optional[int],
  ):
      """Sparse-to-dense preprocessing of Map columns, which are states and metrics.
      For each column of type Map, w/ name X, output two columns.
          Map values are assumed to be scalar. This process is called sparse-to-dense.
          X = {"state_features", "next_state_features", "metrics"}.
          (a) Replace column X with a dense repesentation of the inputted (sparse) map.
              Dense representation is to concatenate map values into a list.
          (b) Create new column X_presence, which is a list of same length as (a) and
              the ith entry is 1 iff the key was present in the original map.
      """
      next_map_udf = make_next_udf(
          multi_steps, MapType(LongType(), FloatType())
      )
      df = df.withColumn(
          "next_state_features", next_map_udf("next_state_features")
      )
      df = df.withColumn("metrics", next_map_udf("metrics"))
      df = make_sparse2dense(df, "state_features", states)
      df = make_sparse2dense(df, "next_state_features", states)
      df = make_sparse2dense(df, "metrics", metrics)
      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 11 for calling a global function with example usage: state_and_metrics_sparse2dense(df, states, metrics, multi_steps) and returns: df