code: def observations_from_map_data(
      experiment: experiment.Experiment,
      map_data: MapData,
      include_abandoned: bool = False,
      map_keys_as_parameters: bool = False,
      limit_rows_per_metric: Optional[int] = None,
      limit_rows_per_group: Optional[int] = None,
  ) -> List[Observation]:
      """Convert MapData to observations.
  
      Converts a MapData object to a list of Observation objects. Pulls arm parameters
      from experiment. Overrides fidelity parameters in the arm with those found in the
      Data object.
  
      Uses a diagonal covariance matrix across metric_names.
  
      Args:
          experiment: Experiment with arm parameters.
          map_data: MapData of observations.
          include_abandoned: Whether data for abandoned trials and arms should
              be included in the observations, returned from this function.
          map_keys_as_parameters: Whether map_keys should be returned as part of
              the parameters of the Observation objects.
          limit_rows_per_metric: If specified, uses MapData.subsample() with
              `limit_rows_per_metric` equal to the specified value on the first
              map_key (map_data.map_keys[0]) to subsample the MapData. This is
              useful in, e.g., cases where learning curves are frequently
              updated, leading to an intractable number of Observation objects
              created.
          limit_rows_per_group: If specified, uses MapData.subsample() with
              `limit_rows_per_group` equal to the specified value on the first
              map_key (map_data.map_keys[0]) to subsample the MapData.
  
      Returns:
          List of Observation objects.
      """
      if (
          limit_rows_per_metric is not None
          or limit_rows_per_group is not None
      ):
          map_data = map_data.subsample(
              map_key=map_data.map_keys[0],
              limit_rows_per_metric=limit_rows_per_metric,
              limit_rows_per_group=limit_rows_per_group,
              include_first_last=True,
          )
      feature_cols = get_feature_cols_from_map_data(map_data)
      observations = []
      arm_name_only = (
          len(feature_cols) == 1
      )  # there will always be an arm name
      # One DataFrame where all rows have all features.
      isnull = map_data.map_df[feature_cols].isnull()
      isnull_any = isnull.any(axis=1)
      incomplete_df_cols = isnull[isnull_any].any()
  
      # Get the incomplete_df columns that are complete, and usable as groupby keys.
      obs_cols_and_map = OBS_COLS.union(map_data.map_keys)
      complete_feature_cols = list(
          obs_cols_and_map.intersection(
              incomplete_df_cols.index[~incomplete_df_cols]
          )
      )
  
      if set(feature_cols) == set(complete_feature_cols):
          complete_df = map_data.map_df
          incomplete_df = None
      else:
          # The groupby and filter is expensive, so do it only if we have to.
          grouped = map_data.map_df.groupby(
              by=complete_feature_cols
              if len(complete_feature_cols) > 1
              else complete_feature_cols[0]
          )
          complete_df = grouped.filter(
              lambda r: ~r[feature_cols].isnull().any().any()
          )
          incomplete_df = grouped.filter(
              lambda r: r[feature_cols].isnull().any().any()
          )
  
      # Get Observations from complete_df
      observations.extend(
          _observations_from_dataframe(
              experiment=experiment,
              df=complete_df,
              cols=feature_cols,
              arm_name_only=arm_name_only,
              map_keys=map_data.map_keys,
              include_abandoned=include_abandoned,
              map_keys_as_parameters=map_keys_as_parameters,
          )
      )
      if incomplete_df is not None:
          # Get Observations from incomplete_df
          observations.extend(
              _observations_from_dataframe(
                  experiment=experiment,
                  df=incomplete_df,
                  cols=complete_feature_cols,
                  arm_name_only=arm_name_only,
                  map_keys=map_data.map_keys,
                  include_abandoned=include_abandoned,
                  map_keys_as_parameters=map_keys_as_parameters,
              )
          )
      return observations
  
imports: from __future__ import annotations
  import json
  import warnings
  from copy import deepcopy
  from typing import Dict, Iterable, List, Optional, Set, Tuple
  import ax.core.experiment
  import numpy
  import pandas
  from ax.core.arm import Arm
  from ax.core.batch_trial import BatchTrial
  from ax.core.data import Data
  from ax.core.map_data import MapData
  from ax.core.types import TCandidateMetadata, TParameterization
  from ax.utils.common.base import Base
  from ax.utils.common.constants import Keys
  from ax.utils.common.typeutils import not_none
label_desc: Use image node_id 5 for calling a global function with example usage: observations_from_map_data(experiment, map_data, include_abandoned, map_keys_as_parameters, limit_rows_per_metric, limit_rows_per_group) and returns: observations