code: def _observations_from_dataframe(
      experiment: experiment.Experiment,
      df: pd.DataFrame,
      cols: List[str],
      arm_name_only: bool,
      map_keys: Iterable[str],
      include_abandoned: bool,
      map_keys_as_parameters: bool = False,
  ) -> List[Observation]:
      """Helper method for extracting observations grouped by `cols` from `df`.
  
      Args:
          experiment: Experiment with arm parameters.
          df: DataFrame derived from experiment Data.
          cols: columns used to group data into different observations.
          map_keys: columns that map dict-like Data
              e.g. `timestamp` in timeseries data, `epoch` in ML training traces.
          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.
  
      Returns:
          List of Observation objects.
      """
      if len(cols) == 0:
          return []
      observations = []
      abandoned_arms_dict = {}
      for g, d in df.groupby(by=cols if len(cols) > 1 else cols[0]):
          obs_kwargs = {}
          if arm_name_only:
              features = {"arm_name": g}
              arm_name = g
              trial_index = None
          else:
              features = dict(zip(cols, g))
              arm_name = features["arm_name"]
              trial_index = features.get("trial_index", None)
  
          if trial_index is not None:
              trial = experiment.trials[trial_index]
              metadata = trial._get_candidate_metadata(arm_name) or {}
              if Keys.TRIAL_COMPLETION_TIMESTAMP not in metadata:
                  if trial._time_completed is not None:
                      metadata[
                          Keys.TRIAL_COMPLETION_TIMESTAMP
                      ] = not_none(trial._time_completed).timestamp()
              obs_kwargs[Keys.METADATA] = metadata
  
              if not include_abandoned and trial.status.is_abandoned:
                  # Exclude abandoned trials.
                  continue
  
              if not include_abandoned and isinstance(
                  trial, BatchTrial
              ):
                  # Exclude abandoned arms from batch trial's observations.
                  if trial.index not in abandoned_arms_dict:
                      # Same abandoned arm names to dict to avoid recomputing them
                      # on creation of every observation.
                      abandoned_arms_dict[
                          trial.index
                      ] = trial.abandoned_arm_names
                  if arm_name in abandoned_arms_dict[trial.index]:
                      continue
  
          obs_parameters = experiment.arms_by_name[
              arm_name
          ].parameters.copy()
          if obs_parameters:
              obs_kwargs["parameters"] = obs_parameters
          for f, val in features.items():
              if f in OBS_KWARGS:
                  obs_kwargs[f] = val
          fidelities = features.get("fidelities")
          if fidelities is not None:
              obs_parameters.update(json.loads(fidelities))
  
          for map_key in map_keys:
              if map_key in obs_parameters or map_keys_as_parameters:
                  obs_parameters[map_key] = features[map_key]
              else:
                  obs_kwargs[Keys.METADATA][map_key] = features[map_key]
          observations.append(
              Observation(
                  features=ObservationFeatures(**obs_kwargs),
                  data=ObservationData(
                      metric_names=d["metric_name"].tolist(),
                      means=d["mean"].values,
                      covariance=np.diag(d["sem"].values ** 2),
                  ),
                  arm_name=arm_name,
              )
          )
      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 1 for calling a global function with example usage: _observations_from_dataframe(experiment, df, cols, arm_name_only, map_keys, include_abandoned, map_keys_as_parameters) and returns: observations, list