      @staticmethod
      def axes_data_get_min_max(data):
          """ Return the minimum and maximum values from list of lists """
          ymin, ymax = list(), list()
          for item in data:
              dataset = list(filter(lambda x: x is not None, item))
              if not dataset:
                  continue
              ymin.append(min(dataset) * 1000)
              ymax.append(max(dataset) * 1000)
          ymin = floor(min(ymin)) / 1000
          ymax = ceil(max(ymax)) / 1000
          logger.trace("ymin: %s, ymax: %s", ymin, ymax)
          return ymin, ymax
  
 
</s> remove             self.ax1.set_xlim(0, xmax)
 </s> add             self.ax1.set_xlim(xmin, xmax) </s> remove         xmax = self.calcs.iterations - 1 if self.calcs.iterations > 1 else 1
 </s> add         xmin = self.calcs.start_iteration
        if self.calcs.start_iteration > 0:
            xmax = self.calcs.iterations + self.calcs.start_iteration
        else:
            xmax = self.calcs.iterations
        xmax = max(1, xmax - 1)
 </s> remove             iterations = range(len(timestamps) - 1)
            rate.extend([(batchsize * 2) / (timestamps[i + 1] - timestamps[i])
                         for i in iterations])
        logger.debug("Calculated totals rate: Item_count: %s", len(rate))
        return rate

    @staticmethod
    def flatten_outliers(data):
        """ Remove the outliers from a provided list """
        logger.debug("Flattening outliers")
        retdata = list()
        samples = len(data)
        mean = (sum(data) / samples)
        limit = sqrt(sum([(item - mean)**2 for item in data]) / samples)
        logger.debug("samples: %s, mean: %s, limit: %s", samples, mean, limit)

        for idx, item in enumerate(data):
            if (mean - limit) <= item <= (mean + limit):
                retdata.append(item)
            else:
                logger.trace("Item idx: %s, value: %s flattened to %s", idx, item, mean)
                retdata.append(mean)
        logger.debug("Flattened outliers")
        return retdata
 </s> add             rate.extend((batchsize * 2) / np.diff(timestamps))
        retval = np.array(rate)
        logger.debug("Calculated totals rate: Item_count: %s", len(retval))
        return retval </s> remove     @property
    def sessions_stats(self):
        """ Return compiled stats """
        compiled = list()
        for sess_idx, ts_data in self.time_stats.items():
            logger.debug("Compiling session ID: %s", sess_idx)
            if self.session.state is None:
                logger.debug("Session state dict doesn't exist. Most likely task has been "
                             "terminated during compilation")
                return None
            iterations = self.session.get_iterations_for_session(sess_idx)
            elapsed = ts_data["end_time"] - ts_data["start_time"]
            batchsize = self.session.total_batchsize.get(sess_idx, 0)
            compiled.append(
                {"session": sess_idx,
                 "start": ts_data["start_time"],
                 "end": ts_data["end_time"],
                 "elapsed": elapsed,
                 "rate": ((batchsize * 2) * iterations) / elapsed if elapsed != 0 else 0,
                 "batch": batchsize,
                 "iterations": iterations})
        compiled = sorted(compiled, key=lambda k: k["session"])
        return compiled

    def compile_stats(self):
        """ Compile sessions stats with totals, format and return """
        logger.debug("Compiling sessions summary data")
        compiled_stats = self.sessions_stats
        if not compiled_stats:
            return compiled_stats
        logger.debug("sessions_stats: %s", compiled_stats)
        total_stats = self.total_stats(compiled_stats)
        compiled_stats.append(total_stats)
        compiled_stats = self.format_stats(compiled_stats)
        logger.debug("Final stats: %s", compiled_stats)
        return compiled_stats

    @staticmethod
    def total_stats(sessions_stats):
        """ Return total stats """
 </s> add     def _total_stats(self):
        """ Compile the Totals stats.
        Totals are fully calculated each time as they will change on the basis of the training
        session.

        Returns
        -------
        dict:
            The Session name, start time, end time, elapsed time, rate, batch size and number of
            iterations for all session ids within the loaded data.
        """ </s> remove     @property
    def time_stats(self):
        """ Return session time stats """
        ts_data = self.session.tb_logs.get_timestamps()
        time_stats = {sess_id: {"start_time": min(timestamps) if timestamps else 0,
                                "end_time": max(timestamps) if timestamps else 0,
                                "datapoints": len(timestamps) if timestamps else 0}
                      for sess_id, timestamps in ts_data.items()}
        return time_stats
 </s> add         Returns
        -------
        dict
            The collated session summary statistics
        """
        timestamps = self._time_stats[session_id]
        elapsed = int(timestamps["end_time"] - timestamps["start_time"])
        batchsize = self._session.batch_sizes.get(session_id, 0)
        retval = dict(
            session=session_id,
            start=timestamps["start_time"],
            end=timestamps["end_time"],
            elapsed=elapsed,
            rate=(((batchsize * 2) * timestamps["iterations"]) / elapsed if elapsed != 0 else 0),
            batch=batchsize,
            iterations=timestamps["iterations"])
        logger.debug(retval)
        return retval
        for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000)
      @staticmethod
      def axes_data_get_min_max(data):
          """ Return the minimum and maximum values from list of lists """
          ymin, ymax = list(), list()
        for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000)
        for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000)
        for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000)
        for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000)
        for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000)
        for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000)
          ymin = floor(min(ymin)) / 1000
          ymax = ceil(max(ymax)) / 1000
          logger.trace("ymin: %s, ymax: %s", ymin, ymax)
          return ymin, ymax
  
keep keep keep keep replace replace replace replace replace replace keep keep keep keep keep
 GUI - Stats optimization (#1067)

* Faster stat loading + caching

* Compress data in cache

* Optimize some calculations

* Vectorize smoothing

* stats.Calculations optimized

* Load latest training data from live iterator

* Add options to training graph 
lib/gui/display_graph.py