code: def read_experiment(
      log_path,
      parameter_set=None,
      job_descriptor="",
      iteration_number=None,
      summary_keys=("train_episode_returns", "eval_episode_returns"),
      verbose=False,
  ):
      """Reads in a set of experimental results from log_path.
  
      The provided parameter_set is an ordered_dict which
        1) defines the parameters of this experiment,
        2) defines the order in which they occur in the job descriptor.
  
      The method reads all experiments of the form
  
      ${log_path}/${job_descriptor}.format(params)/logs,
  
      where params is constructed from the cross product of the elements in
      the parameter_set.
  
      For example:
        parameter_set = collections.OrderedDict([
            ('game', ['Asterix', 'Pong']),
            ('epsilon', ['0', '0.1'])
        ])
        read_experiment('/tmp/logs', parameter_set, job_descriptor='{}_{}')
        Will try to read logs from:
        - /tmp/logs/Asterix_0/logs
        - /tmp/logs/Asterix_0.1/logs
        - /tmp/logs/Pong_0/logs
        - /tmp/logs/Pong_0.1/logs
  
      Args:
        log_path: string, base path specifying where results live.
        parameter_set: An ordered_dict mapping parameter names to allowable values.
        job_descriptor: A job descriptor string which is used to construct the full
          path for each trial within an experiment.
        iteration_number: Int, if not None determines the iteration number at which
          we read in results.
        summary_keys: Iterable of strings, iteration statistics to summarize.
        verbose: If True, print out additional information.
  
      Returns:
        A Pandas dataframe containing experimental results.
      """
      keys = [] if parameter_set is None else list(parameter_set.keys())
      # Extract parameter value lists, one per parameter.
      ordered_values = [parameter_set[key] for key in keys]
  
      column_names = keys + ["iteration"] + list(summary_keys)
      num_parameter_settings = len(
          [_ for _ in itertools.product(*ordered_values)]
      )
      expected_num_iterations = 200
      expected_num_rows = (
          num_parameter_settings * expected_num_iterations
      )
  
      # Create DataFrame with predicted number of rows.
      data_frame = pd.DataFrame(
          index=np.arange(0, expected_num_rows), columns=column_names
      )
      row_index = 0
  
      # Now take their cross product. This generates tuples of the form
      # (p1, p2, p3, ...) where p1, p2, p3 are parameter values for the first,
      # second, etc. parameters as ordered in value_set.
      for parameter_tuple in itertools.product(*ordered_values):
          if job_descriptor is not None:
              name = job_descriptor.format(*parameter_tuple)
          else:
              # Construct name for values.
              name = "-".join(
                  [
                      keys[i] + "_" + str(parameter_tuple[i])
                      for i in range(len(keys))
                  ]
              )
  
          experiment_path = "{}/{}/logs".format(log_path, name)
  
          raw_data, last_iteration = load_statistics(
              experiment_path,
              iteration_number=iteration_number,
              verbose=verbose,
          )
  
          summary = summarize_data(raw_data, summary_keys)
          for iteration in range(last_iteration + 1):
              # The row contains all the parameters, the iteration, and finally the
              # requested values.
              row_data = (
                  list(parameter_tuple)
                  + [iteration]
                  + [summary[key][iteration] for key in summary_keys]
              )
              data_frame.loc[row_index] = row_data
  
              row_index += 1
  
      # The dataframe rows are all read as 'objects', which causes a
      # ValueError when merging below. We cast the numerics to float64s to
      # avoid this.
      for field_name in data_frame.keys():
          try:
              data_frame[field_name] = data_frame[field_name].astype(
                  np.float64
              )
          except ValueError:
              # This will catch any non-numerics that cannot be cast to float64.
              continue
  
      # Shed any unused rows.
      return data_frame.drop(np.arange(row_index, expected_num_rows))
  
file_path: dopamine/dopamine/colab/utils.py
imports: import itertools
  import os
  import pickle
  import sys
  import numpy
  import pandas
  import tensorflow
label_desc: Use image node_id 6 for calling a global function with example usage: read_experiment(log_path, parameter_set, job_descriptor, iteration_number, summary_keys, verbose) and returns: data_frame