  
          fulldata = [item for item in self.calcs.stats.values()]
          self.axes_limits_set(fulldata)
  
          xrng = [x for x in range(self.calcs.iterations)]
          keys = list(self.calcs.stats.keys())
          for idx, item in enumerate(self.lines_sort(keys)):
              if initiate:
                  self.lines.extend(self.ax1.plot(xrng, self.calcs.stats[item[0]],
                                                  label=item[1], linewidth=item[2], color=item[3]))
 
</s> remove             if pos not in self.popup_positions:
                self.popup_positions.append(pos)
 </s> add             if pos not in self._popup_positions:
                self._popup_positions.append(pos) </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         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)
 </s> add         for item in data:  # TODO Handle as array not loop
            ymin.append(np.nanmin(item) * 1000)
            ymax.append(np.nanmax(item) * 1000) </s> remove             init_pos, pos = self.data_popup_check_boundaries(init_pos, pos)
 </s> add             init_pos, pos = self._data_popup_check_boundaries(init_pos, pos)
        if self.calcs.start_iteration > 0:
            end_iteration = self.calcs.start_iteration + self.calcs.iterations
            xrng = list(range(self.calcs.start_iteration, end_iteration))
        else:
            xrng = list(range(self.calcs.iterations))

  
          fulldata = [item for item in self.calcs.stats.values()]
          self.axes_limits_set(fulldata)
  
        if self.calcs.start_iteration > 0:
            end_iteration = self.calcs.start_iteration + self.calcs.iterations
            xrng = list(range(self.calcs.start_iteration, end_iteration))
        else:
            xrng = list(range(self.calcs.iterations))

          keys = list(self.calcs.stats.keys())
          for idx, item in enumerate(self.lines_sort(keys)):
              if initiate:
                  self.lines.extend(self.ax1.plot(xrng, self.calcs.stats[item[0]],
                                                  label=item[1], linewidth=item[2], color=item[3]))
keep keep keep keep 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