code: # -*- coding: utf-8 -*-
  """This module contains all functions and classes for the MLTree. The MLTree buils a tree-like
  structure of the objects in a given repository. This allows the user to access objects in a
  comfortable way allowing for autocompletion (i.e. in Jupyter notebooks).
  
  To use it one can simply call the :py:meth:`pailab.tools.tree.MLTree.add_tree` method to 
  add such a tree to the current repository::
  
      >>from pailab.tools.tree import MLTree
      >>MLTree.add_tree(ml_repo)
  
  After the tree has been added, one can simply use the tree. Here, using autocompletion makes the basic work wih repo objects quite simply.
  Each tree node provides useful functions that can be applied:
  
  - ``load`` loads the object of the given tree node or the child tree nodes of the current node. a
    After calling load the respective nodes have a new attribute ``obj`` that contains the respective loaded object. To load all objects belonging to the models subtree like 
    parameters, evaluations or measures one can call::
  
     >> ml_repo.tree.models.load()
  
  - ``history`` lists the history of all objects of the respective subtree, where history excepts certain parameters such as a range of versions or 
    which repo object information to include. To list th history of all training data just use::
  
      >> ml_repo.tree.training_data.history()
  
  - ``modifications`` lists all objects of the respective subtree that have been modified and no yet been committed.
  
  There are also node dependent function (depending on what object the node represents).
  """
  import logging
  from numpy import load
  from deepdiff import DeepDiff
  from pailab.ml_repo.repo import MLObjectType, MLRepo
  from pailab.ml_repo.repo_objects import RepoInfoKey, DataSet  # pylint: disable=E0401
  from pailab.ml_repo.repo_store import RepoStore  # pylint: disable=E0401
  import pailab.ml_repo.repo_store as repo_store
  import pailab.ml_repo.repo_objects as repo_objects
  logger = logging.getLogger(__name__)
  
  #region collections and items
  
  
  
  class _RepoObjectItem:
  
      def __init__(self, name, ml_repo, repo_obj = None):
          self._name = name
          self._repo = ml_repo
          if repo_obj is not None:
              self.obj = repo_obj
       
      def _set(self, path, items):
          if len(path) > 0:
              if len(path) == 1:
                  setattr(self, path[0], items[0])
                  return
              if hasattr(self, path[0]):
                  getattr(self, path[0])._set(path[1:], items[1:])
              else:
                  setattr(self, path[0], items[0])
                  items[0]._set(path[1:], items[1:])
  
      def load(self, version=repo_store.LAST_VERSION, full_object=False,
              modifier_versions=None, containing_str=None):
              """Loads the object into the tree and stores it in obj member.
              
              Args:
                  version (str, optional): The version of the object to be loaded. Defaults to repo_store.LAST_VERSION.
                  full_object (bool, optional): If True, also the bigobject-members of the object will be loaded and stored. Defaults to False.
                  modifier_versions (dict of str to str, optional): The version of the object that has been created with the objects 
                                  and their respective versions defined in the dict will be loaded. Defaults to None.
                  containing_str (str, optional): The object will only be loaded if the given string is contained in the objects 
                                              name (intended for internal use). Defaults to None.
              """
              if containing_str is None or containing_str in self._name:
                  if self._repo is not None:
                      self.obj = self._repo.get(self._name, version, full_object, modifier_versions, throw_error_not_exist = False)
              for v in self.__dict__.values():
                  if hasattr(v,'load'):
                      v.load(version, full_object, modifier_versions, containing_str)
  
      def modifications(self, commit=False, commit_message=''):
          result = {}
          if self._name is not None:
              try:
                  if self._repo is not None:
                      obj_orig = self._repo.get(
                          self.obj.repo_info[RepoInfoKey.NAME], version=self.obj.repo_info[RepoInfoKey.VERSION])
                      diff = DeepDiff(obj_orig, self.obj,
                                  ignore_order=True)
              except AttributeError:
                  return None
              if len(diff) == 0:
                  return None
              else:
                  if commit and (self._repo is not None):
                      version = self._repo.add(
                          self.obj, message=commit_message)
                      self.obj = self._repo.get(self._name, version=version)
                  result = {self._name: diff}
          for v in self.__dict__.values():
              if hasattr(v, 'modifications'):
                  tmp = v.modifications(commit, commit_message)
                  if tmp is not None:
                      result.update(tmp)
          return result
  
      def history(self, version = (repo_store.FIRST_VERSION,repo_store.LAST_VERSION), 
                  repo_info = [RepoInfoKey.NAME, RepoInfoKey.AUTHOR, RepoInfoKey.COMMIT_DATE, RepoInfoKey.COMMIT_MESSAGE], 
                  obj_data = []):
          history = []
          if self._repo is not None:
              history = self._repo.get(self._name, version = version,  throw_error_not_exist=False)
          if not isinstance(history, list):
              history = [history]
          result = {}
          tmp = []
          for h in history:
              r = {}
              for r_info in repo_info:
                  r[str(r_info)] = h.repo_info[r_info]
              for o_info in obj_data:
                  r[o_info] = obj_data.__dict__[o_info]
              tmp.append(r)
          result[self._name] = tmp
          for v in self.__dict__.values():
              if isinstance(v, _RepoObjectItem):
                  tmp2 = v.history(version, repo_info, obj_data)
                  if tmp2 is not None:
                      result.update(tmp2)
          if len(result) > 0:
              return result
          
  
      def __call__(self, containing_str=None):
          # if len(self.__dict__) == 1:
          if containing_str is not None:
              result = []
              if containing_str in self._name:
                  result.append(self._name)
              for v in self.__dict__.values():
                  if isinstance(v, _RepoObjectItem):
                      d = v(containing_str)
                      if isinstance(d, str):
                          result.append(d)
                      else:
                          result.extend(d)
              return [x for x in result if containing_str in x]
          else:
              return self._name
          return result
  
  
  class _RawDataItem(_RepoObjectItem):
      def __init__(self, name, ml_repo, repo_obj = None):
          super(_RawDataItem,self).__init__(name, ml_repo, repo_obj)
  
      def append(self, x_data, y_data = None):
          """Append data to a RawData object
  
          It appends data to the given RawData object and updates all training and test DataSets which implicitely changed by this update.
  
          Args:
              name (string): name of RawData object
              x_data (numpy matrix): the x_data to append
              y_data (numpy matrix, optional): Defaults to None. The y_data to append
          
          Raises:
              Exception: If the data is not consistent to the RawData (e.g. different number of x-coordinates) it throws an exception.
          """
          logger.info('Start appending ' + str(x_data.shape[0]) + ' datapoints to RawData' + self._name)
          raw_data = self._repo.get(self._name)
          if len(raw_data.x_coord_names) != x_data.shape[1]:
              raise Exception('Number of columns of x_data of RawData object is not equal to number of columns of additional x_data.')
          if raw_data.y_coord_names is None and y_data is not None:
              raise Exception('RawData object does not contain y_data but y_data is given')
          if raw_data.y_coord_names is not None:
              if y_data is None:
                  raise Exception('RawData object has y_data but no y_data is given')
              if y_data.shape[1] != len(raw_data.y_coord_names ):
                  raise Exception('Number of columns of y_data of RawData object is not equal to number of columns of additional y_data.')
          numpy_dict = {'x_data' : x_data}
          if raw_data.y_coord_names is not None:
              numpy_dict['y_data'] =  y_data
          raw_data.n_data += x_data.shape[0]
          old_version = raw_data.repo_info[RepoInfoKey.VERSION]
          new_version = self._repo.add(raw_data)
          self._repo._numpy_repo.append(self._name, old_version, new_version, numpy_dict)
          # now find all datasets which are affected by the updated data
          changed_data_sets = []
          training_data = self._repo.get_training_data(full_object = False)
          if isinstance(training_data, DataSet):
              if training_data.raw_data == self._name and training_data.raw_data_version == repo_store.RepoStore.LAST_VERSION:
                  if training_data.end_index is None or training_data.end_index < 0:
                      training_data.raw_data_version = new_version
                      changed_data_sets.append(training_data)
          test_data = self._repo.get_names(MLObjectType.TEST_DATA)
          for d in test_data:
              data = self._repo.get(d)
              if isinstance(data, DataSet):
                  if data.raw_data == self._name and data.raw_data_version == repo_store.RepoStore.LAST_VERSION:
                      if data.end_index is None or data.end_index < 0:
                          data.raw_data_version = new_version
                          changed_data_sets.append(data)
          self._repo.add(changed_data_sets, 'RawData ' + self._name + ' updated, add DataSets depending om the updated RawData.')
          if hasattr(self, 'obj'):#update current object
              self.obj = self._repo.get(self._name, version=new_version)
          logger.info('Finished appending data to RawData' + self._name)
  
  class _RawDataCollection(_RepoObjectItem):
      @staticmethod
      def __get_name_from_path(path):
          return path.split('/')[-1]
  
      def __init__(self, repo):
          super(_RawDataCollection, self).__init__('raw_data', repo)
          names = repo.get_names(MLObjectType.RAW_DATA)
          for n in names:
              setattr(self, _RawDataCollection.__get_name_from_path(n), _RawDataItem(n, repo))
          
      def add(self, name, data, input_variables = None, target_variables = None):
          """Add raw data to the repository
  
          Arguments:
              data_name {name of data} -- the name of the data added
              data {pandas DataFrame} -- the data as pandas datatable
              input_variables {str or iterable of str} -- column name or iterable of column names defining the input variables of the given data
              target_variables {str or iterable of str} -- column name or iterable of column names defining the target variables of the given data
          
          Keyword Arguments:
              input_variables {list of strings} -- list of column names defining the input variables for the machine learning (default: {None}). If None, all variables are used as input
              target_variables {list of strings} -- list of column names defining the target variables for the machine learning (default: {None}). If None, no target data is added from the table.
          """
          path = 'raw_data/' + name
  
          if input_variables is None:
              input_variables = list(data)
              if not target_variables is None:
                  [input_variables.remove(x) for x in target_variables]
          else:
              if isinstance(input_variables, str):
                  input_variables = [input_variables]
              # check whether the input_variables are included in the data
              if not [item for item in input_variables if item in list(data)] == list(input_variables):
                  raise Exception('RawData does not include at least one column included in input_variables')
        
          if target_variables is not None:
              if isinstance(target_variables, str):
                  target_variables = [target_variables]
              # check if target variables are in list
              if not [item for item in target_variables if item in list(data)] == list(target_variables):
                  raise Exception('RawData does not include at least one column included in target_variables')
              raw_data = repo_objects.RawData(data.loc[:, input_variables].values, input_variables, data.loc[:, target_variables].values, 
                  target_variables, repo_info = {RepoInfoKey.NAME: path})
          else:
              raw_data = repo_objects.RawData(data.loc[:, input_variables].values, input_variables, repo_info = {RepoInfoKey.NAME: path})
          v = self._repo.add(raw_data, 'data ' + path + ' added to repository' , category = MLObjectType.RAW_DATA)
          obj = self._repo.get(path, version=v, full_object = False)
          setattr(self, name, _RawDataItem(path, self._repo, obj))
  
      def add_from_numpy_file(self, name, filename_X, x_names, filename_Y=None, y_names = None):
          path = name
          X = load(filename_X)
          Y = None
          if filename_Y is not None:
              Y = load(filename_Y)
          raw_data =  repo_objects.RawData(X, x_names, Y, y_names, repo_info = {RepoInfoKey.NAME: path})
          v = self._repo.add(raw_data, 'data ' + path + ' added to repository' , category = MLObjectType.RAW_DATA)
          obj = self._repo.get(path, version=v, full_object = False)
          setattr(self, name, _RawDataItem(path, self._repo, obj))
  
  class _TrainingDataCollection(_RepoObjectItem):
  
      @staticmethod
      def __get_name_from_path(path):
          return path.split('/')[-1]
      
      def __init__(self, repo):
          super(_TrainingDataCollection, self).__init__('training_data', None)
          self.__repo = repo # we store ml_repo in __repo to circumvent that obj is loaded from eneric base class
          names = repo.get_names(MLObjectType.TRAINING_DATA)
          for n in names:
              setattr(self, _TrainingDataCollection.__get_name_from_path(n), _RepoObjectItem(n, repo))
          
      def add(self, name, raw_data, start_index=0, 
          end_index=None, raw_data_version='last'):
          #path = 'training_data/' + name
          data_set = repo_objects.DataSet(raw_data, start_index, end_index, 
                  raw_data_version, repo_info = {RepoInfoKey.NAME: name, RepoInfoKey.CATEGORY: MLObjectType.TRAINING_DATA})
          v = self.__repo.add(data_set)
          tmp = self.__repo.get(name, version=v)
          item = _RepoObjectItem(name, self.__repo, tmp)
          setattr(self, name, item)
  
  class _TestDataCollection(_RepoObjectItem):
      @staticmethod
      def __get_name_from_path(path):
          return path.split('/')[-1]
      
      def __init__(self, repo):
          super(_TestDataCollection, self).__init__('test_data', None)
          self.__repo = repo # we store ml_repo in __repo to circumvent that obj is loaded from eneric base class
          names = repo.get_names(MLObjectType.TEST_DATA)
          for n in names:
              setattr(self, _TestDataCollection.__get_name_from_path(n), _RepoObjectItem(n,repo))
          
      def add(self, name, raw_data, start_index=0, 
          end_index=None, raw_data_version='last'):
          data_set = repo_objects.DataSet(raw_data, start_index, end_index, 
                  raw_data_version, repo_info = {RepoInfoKey.NAME: name, RepoInfoKey.CATEGORY: MLObjectType.TEST_DATA})
          v = self.__repo.add(data_set)
          tmp = self.__repo.get(name, version=v)
          item = _RepoObjectItem(name, self.__repo, tmp)
          setattr(self, name, item)
  
  class _MeasureItem(_RepoObjectItem):
      def __init__(self, name, ml_repo, repo_obj = None):
          super(_MeasureItem, self).__init__(name, ml_repo, repo_obj) 
  
  class _JobItem(_RepoObjectItem):
      def __init__(self, name, ml_repo, repo_obj = None):
          super(_JobItem, self).__init__(name, ml_repo, repo_obj) 
  
  class _MeasureCollection(_RepoObjectItem):
      def __init__(self, name, ml_repo):
          super(_MeasureCollection, self).__init__('measures', None)
          names = ml_repo.get_names(MLObjectType.MEASURE)
          for n in names:
              path = n.split('/')[2:]
              items = [None] * len(path)
              for i in range(len(items)-1):
                  items[i] = _RepoObjectItem(path[i], None)
              items[-1] = _MeasureItem(n, ml_repo)
              self._set(path, items)
              #items[-2] = MeasuresOnDataItem
  
  class _EvalCollection(_RepoObjectItem):
      def __init__(self, name, ml_repo):
          super(_EvalCollection, self).__init__('eval', None)
          names = ml_repo.get_names(MLObjectType.EVAL_DATA)
          for n in names:
              path = n.split('/')[2:]
              items = [None] * len(path)
              for i in range(len(items)-1):
                  items[i] = _RepoObjectItem(path[i], None)
              items[-1] = _MeasureItem(n, ml_repo)
              self._set(path, items)
  
  class _TestCollection(_RepoObjectItem):
      def __init__(self, name, ml_repo):
          super(_TestCollection, self).__init__('tests', None)
          names = ml_repo.get_names(MLObjectType.TEST)
          for n in names:
              path = n.split('/')[2:]
              items = [None] * len(path)
              for i in range(len(items)-1):
                  items[i] = _RepoObjectItem(path[i], None)
              items[-1] = _RepoObjectItem(n, ml_repo)
              self._set(path, items)
  
  class _JobCollection(_RepoObjectItem):
      def __init__(self, name, ml_repo, model_name):
          super(_JobCollection, self).__init__('jobs', None)
          names = ml_repo.get_names(MLObjectType.JOB)
          for n in names:
              if model_name in n:
                  path = n.split('/')
                  path = path[path.index('jobs')+1:]
                  items = [None] * len(path)
                  for i in range(len(items)-1):
                      items[i] = _RepoObjectItem(path[i], None)
                  items[-1] = _JobItem(n, ml_repo)
                  self._set(path, items)
  
  class _ModelItem(_RepoObjectItem):
      def __init__(self, name, ml_repo, repo_obj = None):
          super(_ModelItem,self).__init__(name, ml_repo, repo_obj)
          self.model = _RepoObjectItem(name + '/model', ml_repo)
          self.eval = _EvalCollection(name + '/eval', ml_repo)
          self.model_param = _RepoObjectItem(name + '/model_param', ml_repo)
          self.tests = _TestCollection(name + '/tests', ml_repo)
          self.measures = _MeasureCollection(name+ '/measure', ml_repo)
          self.jobs = _JobCollection(name+'/jobs', ml_repo, name)
          if ml_repo._object_exists(name+'/training_stat'):
              self.training_statistic = _RepoObjectItem(name+'/training_stat', ml_repo)
          if ml_repo._object_exists(name+'/training_param'):
              self.training_param = _RepoObjectItem(name + '/training_param', ml_repo)
  
  
      def set_label(self, label_name, version = repo_store.RepoStore.LAST_VERSION, message=''):
          self._repo.set_label(label_name, self._name+ '/model', version, message)
  
  class _LabelCollection(_RepoObjectItem):
      def __init__(self, repo):
          super(_LabelCollection,self).__init__('labels', None)
          names = repo.get_names(MLObjectType.LABEL)
          for n in names:
              #label = ml_repo.get()
              setattr(self, n, _RepoObjectItem(n, repo))
          
  class _ModelCollection(_RepoObjectItem):
      @staticmethod
      def __get_name_from_path(name):
          return name
  
      def __init__(self, repo):
          super(_ModelCollection,self).__init__('models', None)
          names = repo.get_names(MLObjectType.MODEL)
          for n in names:
              setattr(self, _ModelCollection.__get_name_from_path(n), _ModelItem(n, repo))
          self.labels = _LabelCollection(repo)
          
      def add(self, name):
          setattr(self, name, _ModelItem(name,self._repo))
  
  
  
  class _CacheDataCollection(_RepoObjectItem):
  
      @staticmethod
      def __get_name_from_path(path):
          return path.split('/')[-1]
      
      def __init__(self, repo):
          super(_CacheDataCollection, self).__init__('cache', None)
          self.__repo = repo # we store ml_repo in __repo to circumvent that obj is loaded from eneric base class
          names = repo.get_names(MLObjectType.CACHED_VALUE)
          for n in names:
              setattr(self, _CacheDataCollection.__get_name_from_path(n), _RepoObjectItem(n, repo))
  #endregion
  
  
  
  class MLTree:
  
      @staticmethod
      def add_tree(ml_repo):
          """Adds an MLTree to a repository.
  
          Args:
              ml_repo (MLRepo): the repository the tre is added
          """
          setattr(ml_repo, 'tree', MLTree(ml_repo))
          ml_repo._add_triggers.append(ml_repo.tree.reload)
          
      def __create(self):
          self.raw_data = _RawDataCollection(self.__ml_repo)
          self.training_data = _TrainingDataCollection(self.__ml_repo)
          self.test_data = _TestDataCollection(self.__ml_repo)
          self.models = _ModelCollection(self.__ml_repo)
          self.cache = _CacheDataCollection(self.__ml_repo)
  
      def __init__(self, ml_repo):
          self.__ml_repo = ml_repo
          self.__create()
  
      def reload(self, **kwargs):
          """Method to reload the tree after objects have been added or deleted from the repository.
          """
          self.__create()  # todo make this more efficient by just updating collections and items which are affected by this
  
      def modifications(self):
          """Return a dictionary of all objects that were modified but no yet 
          commited to the repository.
          
          Returns:
              dict: dictionary mapping object ids to dictionary of the modified attributes 
          """
          result = {}
          tmp = self.raw_data.modifications()
          if tmp is not None:
              result.update(tmp)
          tmp = self.training_data.modifications()
          if tmp is not None:
              result.update(tmp)
          tmp = self.test_data.modifications()
          if tmp is not None:
              result.update(stmp)
          tmp = self.models.modifications()
          if tmp is not None:
              result.update(tmp)
          if len(result) == 0:
              return None
          return result
  
          
   import numpy as np
  import copy
  import logging
  from IPython.display import display, clear_output
  from collections import defaultdict
  import pailab.analysis.plot as paiplot
  import pailab.analysis.plot_helper as plt_helper
  import ipywidgets as widgets
  
  from pailab import MLObjectType, RepoInfoKey, FIRST_VERSION, LAST_VERSION
  from pailab.ml_repo.repo import NamingConventions
  import pailab.tools.checker as checker
  import pailab.tools.tools as tools
  import pailab.tools.interpretation as interpretation
  import pandas as pd
  
  import matplotlib.pyplot as plt
  import matplotlib.dates as mdates
  
  logger = logging.getLogger(__name__)
  
  # set option so that long lines have a linebreak
  pd.set_option('display.max_colwidth', -1)
  # set widget use to True so that plotlys FigureWidget is used
  paiplot.use_within_widget = True
  
  if paiplot.has_plotly:
      import plotly.graph_objs as go
  
  beakerX = False
  if beakerX:
      from beakerx import TableDisplay
      # from beakerx.object import beakerx
  else:
      def TableDisplay(dt):
          display(dt)
  
  
  class _MLRepoModel:
  
      class _DataModel:
          def __init__(self, ml_repo):
              self._training_data = {}
              self._test_data = {}
              for k in ml_repo.get_names(MLObjectType.TRAINING_DATA):
                  tmp = ml_repo.get(k)
                  self._training_data[k] = tmp.n_data
                  self._x_coord_names = tmp.x_coord_names
                  self._y_coord_names = tmp.y_coord_names
              for k in ml_repo.get_names(MLObjectType.TEST_DATA):
                  tmp = ml_repo.get(k)
                  self._test_data[k] = tmp.n_data
  
          def get_data_names(self):
              result = [k for k in self._test_data.keys()]
              result.extend([k for k in self._training_data.keys()])
              return result
  
          def get_num_data(self, data):
              result = []
              for d in data:
                  if d in self._test_data.keys():
                      result.append(self._test_data[d])
                  elif d in self._training_data.keys():
                      result.append(self._training_data[d])
                  else:
                      raise Exception('Cannot find data ' + d)
              return result
  
      class _ModelModel:
          def __init__(self, ml_repo):
              self.labels = {}  # dictionary label->model and version
              # dictionary (model,version)->labelname or None
              self.model_to_label = defaultdict(lambda: None)
              self._setup_labels(ml_repo)
              self._model_info_table = self._setup_model_info_table(ml_repo)
              self._model_names = ml_repo.get_names(
                  MLObjectType.CALIBRATED_MODEL)
  
          def _setup_labels(self, ml_repo):
              label_names = ml_repo.get_names(MLObjectType.LABEL)
              if label_names is None:
                  return
              if isinstance(label_names, str):
                  label_names = [label_names]
              for l in label_names:
                  label = ml_repo.get(l)
                  self.labels[l] = {'model': label.name,
                                    'version': label.version}
                  self.model_to_label[(label.name, label.version,)] = l
  
          def _setup_model_info_table(self, ml_repo):
              model_rows = []
              model_names = ml_repo.get_names(MLObjectType.CALIBRATED_MODEL)
              for model_name in model_names:
                  models = ml_repo.get(model_name, version=(
                      FIRST_VERSION, LAST_VERSION), full_object=False)
                  if not isinstance(models, list):
                      models = [models]
                  for model in models:
                      tmp = copy.deepcopy(model.repo_info.get_dictionary())
                      tmp['model'] = tmp['name']
                      del tmp['big_objects']
                      del tmp['modifiers']
                      del tmp['modification_info']
                      tmp['label'] = self.model_to_label[(
                          tmp['model'], tmp['version'],)]
                      tmp['widget_key'] = tmp['commit_date'][0:16] + ' | ' + \
                          tmp['author'] + ' | ' + \
                          str(tmp['label']) + ' | ' + tmp['version']
                      model_rows.append(tmp)
              model_info_table = pd.DataFrame(model_rows)
              model_info_table.set_index(['model', 'version'], inplace=True)
              return model_info_table
  
          def get_models(self):
              return self._model_names
  
          def get_info_table(self):
              return self._model_info_table
  
          def setup_error_measure_table(self, ml_repo, data_sets, measures):
              tmp = []
              for measure in measures:
                  for data in data_sets:
                      tmp.append(pd.DataFrame(
                          tools.get_model_measure_list(ml_repo,  measure, data)))
                      tmp[-1].set_index(['model', 'version'], inplace=True)
              result = self.get_info_table()
              tmp.insert(0, result)
              return pd.concat(tmp, axis=1)
  
      class _ConsistencyModel:
          def __init__(self, ml_repo):
              self.tests = checker.Tests.run(ml_repo)
              self.model = checker.Model.run(ml_repo)
              self.data = checker.Data.run(ml_repo)
  
      def __init__(self):
          pass
  
      def set_repo(self, ml_repo):
          self.ml_repo = ml_repo
          self._setup()
  
      def _setup(self):
          self.object_types = {}
          for k in MLObjectType:
              self.object_types[k.value] = self.ml_repo.get_names(k)
          self.data = _MLRepoModel._DataModel(self.ml_repo)
          self.model = _MLRepoModel._ModelModel(self.ml_repo)
          self.consistency = _MLRepoModel._ConsistencyModel(self.ml_repo)
          self._setup_measures()
          self._setup_labels()
          # now set label information into
  
      def _setup_labels(self):  # todo: das hier muss weg
          self.labels = {}
          label_names = self.ml_repo.get_names(MLObjectType.LABEL)
          if label_names is None:
              return
          if isinstance(label_names, str):
              label_names = [label_names]
          for l in label_names:
              label = self.ml_repo.get(l)
              self.labels[l] = {'model': label.name, 'version': label.version}
  
      def _setup_measures(self):
          measure_names = self.ml_repo.get_names(
              MLObjectType.MEASURE_CONFIGURATION)
          if len(measure_names) == 0:
              self.measures = []
          else:
              measure_config = self.ml_repo.get(measure_names[0])
              self.measures = [x for x in measure_config.measures.keys()]
  
      def get_model_statistics(self):
          model_stats = {}
          models = self.ml_repo.get_names(MLObjectType.CALIBRATED_MODEL)
          if isinstance(models, str):
              models = [models]
          for m in models:
              model = self.ml_repo.get(m)
              model_stats[model.repo_info.name] = {
                  'last commit': model.repo_info.commit_date,
                  '#total commits': self.model.get_info_table().shape[0]
              }
          return model_stats
  
      def get_versions(self, name):
          return self.ml_repo.get_history(name, obj_member_fields=[])
  
      # def get_model_parameter(self, model_name)
  
  
  widget_repo = _MLRepoModel()
  
  # region helpers
  
  
  def _add_title_and_border(name):
      def _get_widget(get_widget):
          def wrapper(self):
              return widgets.VBox(children=[
                  # , layout = widgets.Layout(width = '100%')),
                  widgets.HTML(
                      value='<h3 style="Color: white; background-color:#d1d1e0; text-align: center"> ' + name + '</h3>'),
                  get_widget(self),
                  # , layout = widgets.Layout(width = '100%'))
                  widgets.HTML(
                      value='<h3 style="Color: white; background-color:#d1d1e0; text-align: center"> </h3>')
              ], layout=widgets.Layout(padding='0px 0px 0px 0px', overflow_x='auto')  # , overflow_y='auto', )
              )  # layout=widgets.Layout(border='solid 1px'))
          return wrapper
      return _get_widget
  
  
  def _highlight_max(data, color='red'):
      '''
      highlight the maximum in a Series or DataFrame
      '''
      attr = 'color: {}'.format(color)
      # remove % and cast to float
      # data = data.replace('%','', regex=True).astype(float)
      if data.ndim == 1:  # Series from .apply(axis=0) or axis=1
          is_max = data == data.max()
          return [attr if v else '' for v in is_max]
      else:  # from .apply(axis=None)
          is_max = data == data.max().max()
          return pd.DataFrame(np.where(is_max, attr, ''),
                              index=data.index, columns=data.columns)
  
  
  def _highlight_min(data, color='green'):
      '''
      highlight the maximum in a Series or DataFrame
      '''
      attr = 'color: {}'.format(color)
      # remove % and cast to float
      # data = data.replace('%','', regex=True).astype(float)
      if data.ndim == 1:  # Series from .apply(axis=0) or axis=1
          is_max = data == data.min()
          return [attr if v else '' for v in is_max]
      else:  # from .apply(axis=None)
          is_max = data == data.min().min()
          return pd.DataFrame(np.where(is_max, attr, ''),
                              index=data.index, columns=data.columns)
  
  
  class _TableViewer:
      def __init__(self, table, table_name, selected_columns=None):
          self._table = table
          self._table_name = table_name
          self._columns = table.columns
          if selected_columns is None:
              self._selected_columns = self._columns
          else:
              self._selected_columns = selected_columns
  
          self._selected_columns = widgets.SelectMultiple(
              options=self._columns, value=self._selected_columns)
          self._output = widgets.Output()
  
          self._settings = widgets.HBox(children=[])
          self._tab = widgets.Tab(children=[self._output, self._settings], title=[
                                  'Table', 'Table Settings'])
  
          self._button_update = widgets.Button(description='update')
          self._button_update.on_click(self.get_overview)
  
      def get_overview(self, d):
          with self._output:
              clear_output(wait=True)
              # , orient='index'))
              TableDisplay(self._table[self._selected_columns.value])
  
      def get_widget(self):
          return self._tab
  
  
  class _ObjectCategorySelector:
  
      def __init__(self, *args, **kwargs):
          selection = []
          for k, v in widget_repo.object_types.items():
              if len(v) > 0:
                  selection.append(k + ' (' + str(len(v)) + ')')
          if 'layout' not in kwargs.keys():
              kwargs['layout'] = widgets.Layout(width='300px', height='250px')
          kwargs['value'] = []
          self._selector = widgets.SelectMultiple(options=selection,
                                                  # value = [selection[0]],
                                                  **kwargs)
  
      def get_selection(self):
          return [k.split(' ')[0] for k in self._selector.value]
  
      def get_widget(self):
          return widgets.VBox(children=[
                              widgets.Label(value='Object Types'),
                              self._selector
                              ]
                              )
  
  
  class _DataSelector:
      """Widget to select training and test data.
      """
  
      def __init__(self, **kwargs):
          names = widget_repo.data.get_data_names()
          # if len(names) > 0:
          self._selection_widget = widgets.SelectMultiple(
              options=names, value=[names[0]], **kwargs)
  
      def get_widget(self):
          return widgets.VBox(children=[widgets.Label(value='Data'), self._selection_widget])
  
      def get_selection(self):
          return self._selection_widget.value
  
  
  class _DataSelectorWithVersion:
      """Widget to select training and test data.
      """
  
      def __init__(self, display_selection=True, **kwargs):
          names = widget_repo.data.get_data_names()
          self._update_callbacks = []
          self._display_selection = display_selection
          self._selection = {}
          self._selection_options = {}
          self._key_to_version = {}
          self._updating_version = {}
          for n in names:
              self._selection[n] = []
              self._selection_options[n] = []
              self._key_to_version[n] = {}
          self._selected_overview = widgets.Output()
          self._selection_data = widgets.Dropdown(
              options=names, value=None, **kwargs)
  
          self._selection_data.observe(self._update_version, names='value')
  
          self._selection_version = widgets.SelectMultiple(
              options=[], value=[], **kwargs)
          self._selection_version.observe(
              self._display_selected_overview, names='value')
  
      def _get_state(self):
          return self._selection, self._selection_options,  self._key_to_version
  
      def _set_state(self, state):
          self._selection = state[0]
          self._selection_options = state[1]
          self._key_to_version = state[2]
  
      def _set_update_callback(self, cb):
          """Set a callback (called at every update of this widget)
  
          Args:
              cb (function): Callback function called at every update.
          """
          self._update_callbacks.append(cb)
  
      def _update_version(self, change):
          self._updating_version = True
          data_selected = self._selection_data.value
          tmp = widget_repo.ml_repo.get_history(data_selected)
          key_to_version = {}
          versions = []
          for x in tmp:
              key = x['repo_info']['commit_date'][0:16] + ' | ' + \
                  x['repo_info']['author'] + ' | ' + x['repo_info']['version']
              key_to_version[key] = x['repo_info']['version']
              versions.append(key)
          self._key_to_version[data_selected] = key_to_version
          self._selection_version.options = versions
          self._selection_version.value = self._selection_options[data_selected]
          for cb in self._update_callbacks:
              cb(change)
          self._updating_version = False
          # self._selection[self._selection_data.value] = [x for x in self._selection_version.value]
  
      def _display_selected_overview(self, change):
          if self._updating_version:
              return
          data_selected = self._selection_data.value
          key_to_version = self._key_to_version[data_selected]
          self._selection[data_selected] = [key_to_version[x]
                                            for x in self._selection_version.value]
          self._selection_options[data_selected] = [
              x for x in self._selection_version.value]
          tmp = {}
          tmp['data'] = []
          tmp['version'] = []
          for n, x in self._selection.items():
              for y in x:
                  tmp['data'].append(n)
                  tmp['version'].append(y)
          for cb in self._update_callbacks:
              cb(change)
          with self._selected_overview:
              clear_output(wait=True)
              display(pd.DataFrame.from_dict(tmp))
  
      def get_widget(self):
          if self._display_selection:
              return widgets.VBox(children=[widgets.Label(value='Data'), self._selection_data,
                                            widgets.Label(
                                                value='Versions'), self._selection_version,
                                            self._selected_overview, ])
          else:
              return widgets.VBox(children=[widgets.Label(value='Data'), self._selection_data,
                                            widgets.Label(value='Versions'), self._selection_version])
  
      def get_selection(self):
          return self._selection
  
      def get_data(self):
          data = {}
          for d_name, d_v in self._selection.items():
              if len(d_v) > 0:
                  data[d_name] = d_v
          return data
  
  
  class _ModelSelectorWithVersion:
  
      @staticmethod
      def _filter_models(labels=None, commit_start=None, commit_end=None, authors=None, model_versions=None):
          """Filter the model table according to the given attributes.
  
          Args:
              labels ([str or iterable of str], optional): If set, returns only models with the selected labels. Defaults to None.
              commit_start (str, optional): String of earliest commit date.. Defaults to None.
              commit_end (str, optional): String of latest commit date. Defaults to None.
              authors (str or iterable of str, optional): If set it return only the models with the corresponding author(s). Defaults to None.
              model_versions (str or iterable of str, optional): If set only modes with respective version(s) are returned. Defaults to None.
  
          Returns:
              pandas DataFrame: The correspondign models.
          """
          result = widget_repo.model.get_info_table()
          if labels is not None:
              if isinstance(labels, str):
                  result = result[result['label'] == labels]
              else:
                  result = result[result['label'].isin(labels)]
          if commit_start is not None:
              result = result[result['commit_date'] >= commit_start]
          if commit_end is not None:
              result = result[result['commit_date'] <= commit_end]
          if authors is not None:
              if isinstance(authors, str):
                  result = result[result['author'] == authors]
              else:
                  result = result[result['author'].isin(authors)]
          if model_versions is not None:
              if isinstance(model_versions, str):
                  result = result[result['version'] == model_versions]
              else:
                  result = result[result['version'].isin(model_versions)]
          return result
  
      def __init__(self,  display_selection=True, **kwargs):
          self._display_selection = display_selection
          self._selection = defaultdict(list)
          self._selection_model_name = widgets.Dropdown(
              options=widget_repo.model.get_models(), value=None, **kwargs)
          self._selection_model_name.observe(
              self._selected_model_changes, names='value')
  
          self._selection_version = widgets.SelectMultiple(
              options=[], value=[], rows=8, layout=widgets.Layout(width="100%"), **kwargs)
  
          self._selected_overview = widgets.Output()
          self._selection_version.observe(
              self._selected_version_changed, names='value')
  
          self._model_changed_callable = None
  
          # Filtering
          #
          labels = widget_repo.ml_repo.get_names(MLObjectType.LABEL)
          self._label_selector = widgets.SelectMultiple(options=labels)
          self._commit_data_start = widgets.DatePicker()
          self._commit_data_end = widgets.DatePicker()
          self._author_selector = widgets.SelectMultiple(
              options=widget_repo.model.get_info_table()['author'].unique())
          self._apply_button = widgets.Button(description='Apply')
          self._apply_button.on_click(self._apply_filter)
          self._clear_button = widgets.Button(description='Clear')
          self._clear_button.on_click(self._clear_filter)
          self._filter = widgets.VBox(children=[
              widgets.Label(value='Labels'),
              self._label_selector,
              widgets.Label(value='Commit Start'),
              self._commit_data_start,
              widgets.Label(value='Commit End'),
              self._commit_data_end,
              widgets.Label(value='Authors'),
              self._author_selector,
              widgets.HBox(children=[
                  self._apply_button,
                  self._clear_button])
          ]
          )
  
      def get_models(self):
          """Returns all selected models as list of tuples (first element is model name, second model version)
          """
          models = widget_repo.model.get_info_table()
          result = {}
          for k, v in self._selection.items():
              if len(v) > 0:
                  result[k] = [models[models['widget_key'] == w].index[0][1]
                               for w in v]
          return result
  
      def observe_model_change(self, handler):
          """Setup a handler when the model trait changed
  
          Args:
              handler (callable): A callable that is called when the model trait changes.
          """
          self._model_changed_callable = handler
  
      def _selected_model_changes(self, change):
          self._update_version(change)
          if self._model_changed_callable is not None:
              self._model_changed_callable(change)
  
      def _selected_version_changed(self, change):
          self._display_selected_overview(change)
  
      def _apply_filter(self, dummy):
          self._updating_version = True
          data_selected = self._selection_model_name.value
          labels = self._label_selector.value
          if len(labels) == 0:
              labels = None
          if self._commit_data_start.value is None:
              commit_start = None
          else:
              commit_start = str(self._commit_data_start.value)
          if self._commit_data_end.value is None:
              commit_end = None
          else:
              commit_end = str(self._commit_data_end.value)
          authors = None
          if len(self._author_selector.value) > 0:
              authors = self._author_selector.value
          models = _ModelSelectorWithVersion._filter_models(labels=labels, authors=authors,
                                                            commit_start=commit_start, commit_end=commit_end)
          self._selection_model_name.options = [
              x for x in models['name'].unique()]
          models = models[models['name'] == data_selected]
          widget_keys = models['widget_key'].values
          self._selection_version.options = [x for x in models['widget_key']]
          self._selection_version.value = [
              x for x in self._selection[data_selected] if x in widget_keys]
          self._updating_version = False
  
      def _clear_filter(self, dummy):
          self._commit_data_start.value = None
          self._commit_data_end.value = None
          self._author_selector.value = []
          self._label_selector.value = []
          self._apply_filter(dummy)
  
      def _update_version(self, change):
          if change['old'] is not None:
              pass
          self._updating_version = True
          data_selected = self._selection_model_name.value
          models = widget_repo.model.get_info_table()
          models = models[models['name'] == data_selected]
          self._selection_version.options = [x for x in models['widget_key']]
          self._selection_version.value = self._selection[data_selected]
          self._updating_version = False
  
      def _update_selected_versions(self, change):
          data_selected = self._selection_model_name.value
          # now handle changes of version selection: Remove versions that have been
          # deselected and add versions that have been selected
          old = set(change['old'])
          new = set(change['new'])
          # remove versions that have been deselected
          diff = old-new
          self._selection[data_selected] = list(
              set(self._selection[data_selected])-diff)
          # add new elements
          diff = new - old
          self._selection[data_selected].extend(diff)
  
      def _display_selected_overview(self, change):
          if self._updating_version:
              return
          self._update_selected_versions(change)
          versions = []
          for n, x in self._selection.items():
              versions.extend(x)
          with self._selected_overview:
              clear_output(wait=True)
              models = widget_repo.model.get_info_table()
              display(models[models['widget_key'].isin(versions)])
  
      def get_widget(self):
          filter_widget = widgets.Accordion(
              children=[self._filter], selected_index=None)
          filter_widget.set_title(0, 'Filter')
          if self._display_selection:
              return widgets.VBox(children=[
                  widgets.VBox(children=[
                      widgets.Label(value='Model'),
                      self._selection_model_name,
                      widgets.Label(value='Versions'),
                      self._selection_version,
                      self._selected_overview,
                  ]
                  ),
                  filter_widget])
  
          else:
              return widgets.VBox(children=[
                  widgets.VBox(children=[
                                  widgets.Label(value='Model'),
                                  self._selection_model_name,
                                  widgets.Label(value='Versions'),
                                  self._selection_version
                                  ]
                               ),
                  filter_widget])
  
  
  class _ModelAndDataSelectorWithVersion:
      """Widget to select a model together with data used in conjunction with the selected model.
  
      Returns:
          [type]: [description]
      """
  
      def __init__(self,  display_selection=True, **kwargs):
          self._display_selection = display_selection
          names = widget_repo.model.get_models()
          self._data = _DataSelectorWithVersion(display_selection=False)
          self._model = _ModelSelectorWithVersion(display_selection=False)
          self._data._set_update_callback(self._display_selected_overview)
          self._selected_overview = widgets.Output()
  
      def get_models(self):
          """Returns all selected models as dictionary from model to list of selected model's versions
          """
          return self._model.get_models()
  
      def get_data(self):
          return self._data.get_data()
  
      def _display_selected_overview(self, change):
          # if self._updating_version:
          #    return
          # data_selected = self._selection_data.value
          # key_to_version = self._key_to_version[data_selected]
          # self._selection[data_selected] = [key_to_version[x] for x in self._selection_version.value]
          # self._selection_options[data_selected] = [x for x in self._selection_version.value]
          # tmp ={}
          # tmp['model'] = []
          # tmp['model version'] =[]
          # tmp['data'] = []
          # tmp['data version'] =[]
          # for n, x in self._selection.items():
          #     for y in x:
          #         for data_name, data_versions in self._model_to_data_states[n][0].items():
          #             for data_version in data_versions:
          #                 tmp['model'].append(n)
          #                 tmp['model version'].append(y)
          #                 tmp['data'].append(data_name)
          #                 tmp['data version'].append(data_version)
  
          # with self._selected_overview:
          #     clear_output(wait = True)
          #     df = pd.DataFrame.from_dict(tmp)
          #     df = df[['model', 'model version', 'data', 'data version']]
          #     #arrays=[tmp['model'],tmp['model version'], tmp['data']]
          #     #df = pd.DataFrame([tmp['data version']], index=arrays)
          #     #multi_index = pd.MultiIndex.from_arrays(arrays, names=('model','model version', 'data', 'data version'))
          #     #df.reindex(index = multi_index)
          #     display(df)
          pass
  
      def get_widget(self):
          model_selection = widgets.Accordion(
              children=[self._model.get_widget()])
          model_selection.set_title(0, 'Model')
          model_selection.selected_index = None
          data_selection = widgets.Accordion(children=[self._data.get_widget()])
          data_selection.set_title(0, 'Data')
          data_selection.selected_index = None
          if self._display_selection:
              return widgets.VBox(children=[
                  model_selection,
                  data_selection,
                  self._selected_overview, ])
          else:
              return widgets.VBox(children=[
                  model_selection,
                  data_selection])
  
  
  class _MeasureSelector:
      """Widget to select measures.
      """
  
      def __init__(self, **kwargs):
          self._selection_widget = widgets.SelectMultiple(
              options=widget_repo.measures, **kwargs)
  
      def get_widget(self):
          return widgets.VBox(children=[widgets.Label(value='Measures'), self._selection_widget])
  
      def get_selection(self):
          return self._selection_widget.value
  
  # endregion
  
  
  class ObjectOverviewList:
      def __init__(self, beakerX=False):
          self._categories = _ObjectCategorySelector(
              layout=widgets.Layout(width='250px', height='250px'))
          self._repo_info = widgets.SelectMultiple(
              options=[k.value for k in RepoInfoKey], value=['category', 'name', 'commit_date', 'version'],
              layout=widgets.Layout(width='200px', height='250px', margin='10px')
          )
          # self._settings = widgets.HBox(children=[self.categories, self._repo_info])
  
          self._button_update = widgets.Button(description='update')
          self._button_update.on_click(self.get_overview)
  
          self._output = widgets.Output(layout=widgets.Layout(
              height='300px', width='1000px', overflow_y='auto',  overflow_x='auto'))
          self._input_box = widgets.HBox(
              children=[
                  self._categories.get_widget(),
                  widgets.VBox(children=[
                      widgets.Label(value='Info Fields'),
                      self._repo_info
                  ]
                  ),
                  widgets.VBox(children=[
                      self._button_update,
                      self._output
                  ],
                      layout=widgets.Layout(margin='10px 10px 10px 10px')
                  )
              ]
          )
  
      def get_overview(self, d):
          result = {}
          for info in self._repo_info.value:
              result[info] = []
  
          for k in self._categories.get_selection():
              for n in widget_repo.object_types[k]:
                  obj = widget_repo.ml_repo.get(n)
                  for info in self._repo_info.value:
                      if isinstance(obj.repo_info[info], MLObjectType):
                          result[info].append(obj.repo_info[info].value)
                      else:
                          result[info].append(str(obj.repo_info[info]))
          with self._output:
              clear_output(wait=True)
              TableDisplay(pd.DataFrame.from_dict(result))  # , orient='index'))
  
      @_add_title_and_border('Object Overview')
      def get_widget(self):
          return self._input_box
  
  
  class ObjectView:
  
      def _setup_names(self, change=None):
          names = []
          for k in self._categories.get_selection():
              names.extend(widget_repo.ml_repo.get_names(k))
          self._names.options = names
  
      def __init__(self):
          self._categories = _ObjectCategorySelector()
          self._names = widgets.SelectMultiple(
              options=[]
          )
          self._setup_names()
          self._categories.observe(self._setup_names, 'value')
  
          self._button_update = widgets.Button(description='show history')
          self._button_update.on_click(self.show_history)
          self._output = widgets.Output()
          self._input_box = widgets.HBox(
              children=[self._categories.get_widget(), self._names, self._button_update, self._output], layout=widgets.Layout(border='solid 1px')
          )
  
      def show_history(self, d):
          result = {RepoInfoKey.NAME.value: [],
                    RepoInfoKey.AUTHOR.value: [],
                    RepoInfoKey.VERSION.value: [],
                    RepoInfoKey.COMMIT_DATE.value: []}
          for k in self._names.value:
              history = widget_repo.ml_repo.get_history(k)
              for l in history:
                  for m in result.keys():
                      result[m].append(l['repo_info'][m])
          with self._output:
              clear_output(wait=True)
              TableDisplay(pd.DataFrame.from_dict(result))
  
      @_add_title_and_border('Object View')
      def get_widget(self):
          return self._input_box
  
  
  class RepoOverview:
      def __init__(self):
          self._repo_name = widgets.HTML(
              value='<div style="background-color:#c2c2d6"><h4 stype="text-align: center"> Repository: '
              + widget_repo.ml_repo._config['name'] + '</h4>')  # , margin = '0px 0px 0px 0px'))
          self._data_statistics = widgets.Output(
              layout=widgets.Layout(width='450px', height='450px'))
          self._plot_data_statistics()
          self._measures = widgets.Output(
              layout=widgets.Layout(width='450px', height='450px'))
          self._consistency = self._setup_consistency()
          self._labels = self._setup_labels()
          self._model_stats = self._setup_model_stats()
  
      # check consistency
  
      def _setup_consistency(self):
          def create_consistency_html(**kwargs):
              result = '<div style="background-color:#c2c2d6">'
              result += '<h4 stype="text-align: center">Consistency</h4>'
              for k, v in kwargs.items():
                  if len(v) > 0:
                      result += '<p style="background-color:red"> ' + \
                          str(v) + ' ' + k + ' issues found!</p>'
                  else:
                      result += '<p style="background-color:lightgreen">No ' + k + ' issues found.</p>'
              result += '</div>'
              return result
  
          return widgets.HTML(create_consistency_html(model=widget_repo.consistency.model,
                                                      test=widget_repo.consistency.tests,
                                                      data=widget_repo.consistency.data),
                              layout=widgets.Layout(margin='0% 0% 0% 0%', width='400px'))
  
      def _setup_labels(self):
          header = widgets.HTML(
              '<div style="background-color:#c2c2d6"><h4 stype="text-align: center">Labels</h4>')
          label_output = None
  
          if len(widget_repo.labels) > 0:
              label_output = widgets.Output(
                  layout=widgets.Layout(width='400px', height='100px', overflow_y='auto', overflow_x='auto'))
              with label_output:
                  clear_output(wait=True)
                  display(pd.DataFrame.from_dict(
                      widget_repo.labels, orient='index'))
          else:
              label_output = widgets.HTML(
                  '<div style="background-color:#ff4d4d"><h4 stype="text-align: center">No labels defined.</h4>')
  
          return widgets.VBox(children=[header, label_output])
  
      def _setup_model_stats(self):
          header = widgets.HTML(
              '<div style="background-color:#c2c2d6"><h4 stype="text-align: center">Models</h4>')
          model_stats_output = widgets.Output(
              layout=widgets.Layout(width='400px', height='100px', overflow_y='auto', overflow_x='auto'))
          with model_stats_output:
              clear_output(wait=True)
              display(pd.DataFrame.from_dict(
                  widget_repo.get_model_statistics(), orient='index'))
          return widgets.VBox(children=[header, model_stats_output])
  
      def _plot_data_statistics(self):
          data_names = widget_repo.data.get_data_names()
          data_num_points = widget_repo.data.get_num_data(data_names)
          with self._data_statistics:
              clear_output(wait=True)
              plt.rcdefaults()
              _, ax = plt.subplots()
              y_pos = np.arange(len(data_names))
              ax.barh(y_pos, data_num_points, align='center',
                      color='green', ecolor='black')
              ax.set_yticks(y_pos)
              ax.set_yticklabels(data_names, rotation=45, va='top')
              ax.invert_yaxis()
              ax.set_xlabel('number of datapoints')
              ax.set_title('Datasets')
              plt.show()
  
      def _plot_measures(self):
          with self._measures:
              clear_output(wait=True)
              plt.rcdefaults()
              fig = plt.figure()
              ax = fig.add_subplot(111)
              for data in widget_repo.data.get_data_names():
                  for measure in widget_repo.measures:
                      error = widget_repo.model.setup_error_measure_table(
                          widget_repo.ml_repo, [data], [measure])
                      error = error.sort_values(by='commit_date')
                      plt.plot(error['commit_date'],
                               error[measure + ', ' + data], '-x', label=measure + ', ' + data)
              plt.xlabel('commit date')
              ax.grid(True)
              ax.format_xdata = mdates.DateFormatter('%Y-%m-%d')
              for label in ax.get_xticklabels():
                  label.set_rotation(40)
                  label.set_horizontalalignment('right')
              # fig.autofmt_xdate()
              plt.legend()
              ax.set_title('Measures')
              # plt.setp(ax.get_xticklabels(), ha="right", rotation=45)
              plt.show()
              # plt.set_title('Measures')
  
      @_add_title_and_border('Repository Overview')
      def get_widget(self):
          self._plot_measures()
          return widgets.HBox(children=[
              widgets.VBox(children=[self._repo_name, self._model_stats, self._labels, self._consistency],
                           layout=widgets.Layout(width='400px')),
              widgets.VBox(children=[self._measures]),
              widgets.VBox(children=[self._data_statistics])
          ],
              layout=widgets.Layout(width='100%', height='100%')
          )
  
  
  class MeasureView:
      def __init__(self, beakerX=False):
          self._data = _DataSelector()
          self._measures = _MeasureSelector()
          self._repo_info = widgets.SelectMultiple(
              options=[k.value for k in RepoInfoKey], value=['category', 'name', 'commit_date', 'version'], layout=widgets.Layout(width='200px', height='250px')
          )
          self._output = widgets.Output(layout=widgets.Layout(
              width='1000px', height='450px', overflow_y='auto', overflow_x='auto'))
          self._button_update = widgets.Button(description='update')
          self._button_update.on_click(self.get_measures)
  
      def _get_columns_selected(self):
          columns = [x for x in self._repo_info.value]
          for data in self._data.get_selection():
              for m in self._measures.get_selection():
                  columns.append(m+', '+data)
          return columns
  
      @_add_title_and_border('Measure View')
      def get_widget(self):
          self._tab = widgets.Tab(children=[
              self._output,
              widgets.HBox(children=[
                  self._data.get_widget(),
                  self._measures.get_widget(),
                  widgets.VBox(children=[
                      widgets.Label(
                          value='Model Columns'),
                      self._repo_info]
                  ),
                  self._button_update
              ])
          ],
              title=['Table', 'Settings']
          )
          self._tab.set_title(0, 'Table')
          self._tab.set_title(1, 'Settings')
          return self._tab
  
      def get_measures(self, d):
          self._tab.selected_index = 0
          tmp = widget_repo.model.setup_error_measure_table(
              widget_repo.ml_repo, self._data.get_selection(), self._measures.get_selection())
          columns = [c for c in tmp.columns if c in self._get_columns_selected()]
          tmp2 = tmp[columns]
          with self._output:
              clear_output(wait=True)
              # apply highlighting to floating columns only
              floats = [x.kind == 'f' for x in tmp2.dtypes]
              float_columns = tmp2.columns[floats]
              TableDisplay(tmp2.style.apply(_highlight_max, subset=float_columns).apply(
                  _highlight_min, subset=float_columns))  # , orient='index'))
  
  
  class ConsistencyChecker:
  
      def _consistency_check(self):
          self._test_results = checker.Tests.run(self._ml_repo)
          self._model_results = checker.Model.run(self._ml_repo)
          self._data_results = checker.Data.run(self._ml_repo)
  
      def __init__(self, ml_repo, beakerX=False):
          self._ml_repo = ml_repo
  
          self._overview_output = widgets.Output()
  
          self._button_update = widgets.Button(description='update')
          self._button_update.on_click(self.show_checks)
  
          self._widget_main = widgets.VBox(
              children=[self._button_update, self._overview_output]
          )
  
      def show_checks(self, d):
          self._consistency_check()
          with self._overview_output:
              clear_output(wait=True)
              print('test issues: ' + str(len(self._test_results)) + ', model issues: ' +
                    str(len(self._model_results)) + ', data issues: ' + str(len(self._data_results)))
              result = {'test': [], 'message': [], 'model': [], 'type': []}
              for k, v in self._test_results.items():
                  for a, b in v.items():
                      result['type'].append('test')
                      result['model'].append(k)
                      result['test'].append(a)
                      result['message'].append(b)
              for k, v in self._model_results.items():
                  for a, b in v.items():
                      result['type'].append('model')
                      result['model'].append(k)
                      result['test'].append(a)
                      result['message'].append(b)
              for k, v in self._data_results.items():
                  for a, b in v.items():
                      result['type'].append('data')
                      result['model'].append(k)
                      result['test'].append(a)
                      result['message'].append(b)
  
              display(pd.DataFrame.from_dict(result))
  
      @_add_title_and_border('Consistency')
      def get_widget(self):
          return self._widget_main
  
  
  class ModelErrorHistogram:
      """Class to plot histograms of model errors.
        Please make sure that if you use plotly, also the jupyter plotlywidgets are installed via:
          jupyter nbextension install --py --sys-prefix plotlywidget
        otherwise you may encounter problems using this class.
      """
  
      def __init__(self):
          self._model_data_selector = _ModelAndDataSelectorWithVersion(
              display_selection=False)
  
          self._update_button = widgets.Button(description='update')
          self._update_button.on_click(self._plot)
          self._output = widgets.Output()
          self._coord = widgets.SelectMultiple(
              options=widget_repo.data._y_coord_names,
              value=[widget_repo.data._y_coord_names[0]],
              disabled=False
          )
  
      def _plot(self, d):
          with self._output:
              clear_output(wait=True)
              display(go.FigureWidget(paiplot.histogram_model_error(widget_repo.ml_repo, self._model_data_selector.get_models(),
                                                                    self._model_data_selector.get_data(), y_coordinate=self._coord.value)))
  
      @_add_title_and_border('Pointwise Model Error Histogram')
      def get_widget(self):
          y_coord = widgets.Accordion(children=[self._coord])
          y_coord.set_title(0, 'Y-coordinates')
          return widgets.HBox(children=[
              widgets.VBox(children=[
                  self._model_data_selector.get_widget(),
                  y_coord,
                  self._update_button
              ]),
              self._output
          ])
  
  
  class ModelErrorConditionalHistogram:
      """Plots the distribution of input data along a given axis for the largest absolute pointwise errors in comparison to the distribution of all data.
      """
  
      def __init__(self):
          self._data_model_selection = _ModelAndDataSelectorWithVersion(
              display_selection=False)
          self._update_button = widgets.Button(description='update')
          self._update_button.on_click(self._plot)
          self._output = widgets.Output()
          self._recommendation_output = widgets.Output()
          self._recommendation_table = None
          self._output_tab = widgets.Tab(children=[self._output,
                                                   self._recommendation_output])
          self._output_tab.set_title(0, 'histograms')
          self._output_tab.set_title(1, 'recommendations')
          self._quantile = widgets.FloatSlider(
              value=10,
              min=1,
              max=50,
              step=1,
              readout=True,
              readout_format='.2f',
          )
          self._coord = widgets.Select(
              options=widget_repo.data._y_coord_names,
              value=widget_repo.data._y_coord_names[0],
              disabled=False
          )
          self._x_coord = widgets.Select(
              options=widget_repo.data._x_coord_names,
              value=widget_repo.data._x_coord_names[0],
              disabled=False
          )
          self._accordion = widgets.Accordion(children=[
              self._get_selection_widget(),
              self._get_recommendation_widget()
          ])
          self._accordion.set_title(0, 'Selection')
          self._accordion.set_title(1, 'Recommendation')
  
      def _get_selection_widget(self):
          coordinate_selection = widgets.Accordion(children=[
              widgets.VBox(children=[
                  widgets.Label(value='y-coordinates'),
                  self._coord,
                  widgets.Label(value='x-coordinates'),
                  self._x_coord])
          ])
          coordinate_selection.set_title(0, 'Coordinates')
          return widgets.VBox(children=[
              self._data_model_selection.get_widget(),
              coordinate_selection,
              self._quantile,
              self._update_button])
  
      def _get_recommendation_widget(self):
          self._update_recommendation = widgets.Button(description='update')
          self._max_num_recommendations = widgets.IntText(value=20,
                                                          description='maximum number of recommendations')
          self._cache_in_repo = widgets.Checkbox(
              value=True, description='cache MMD in repo')
          self._scale = widgets.Checkbox(
              value=True, description='scale x-values to zero mean and unit variance')
          self._update_recommendation.on_click(self._recommend)
          self._kernel_selection = widgets.Dropdown(options=[
              'rbf', 'linear', 'polynomial', 'sigmoid', 'laplacian', 'chi2'
          ],
              value='rbf',
              description='kernel')
          self._gamma = widgets.FloatText(value=1.0, description='gamma')
          self._gamma_for_kernel = [
              'rbf', 'polynomial', 'sigmoid', 'laplacian', 'chi2']
          self._kernel_selection.observe(self._on_kernel_change, names='value')
          self._recommendation_selection = widgets.IntText(
              description='recommendation id')
          self._recomendation_selection_apply = widgets.Button(
              description='apply recommendation')
          self._recomendation_selection_apply.on_click(self._apply_recommend)
          return widgets.VBox(children=[
              self._max_num_recommendations,
              self._cache_in_repo,
              self._scale,
              self._kernel_selection,
              self._gamma,
              self._update_recommendation,
              self._recommendation_selection,
              self._recomendation_selection_apply
          ])
          self._recommendation_table = None
  
      def _on_kernel_change(self, d):
          if self._kernel_selection in self._gamma_for_kernel:
              self._gamma.disabled = False
          else:
              self._gamma.disabled = True
  
      def _apply_recommend(self, d):
          if self._recommendation_table is None:
              logger.error(
                  'Recommendation table is empty, please first update the recommendation.')
              with self._output:
                  clear_output(wait=True)
                  print(
                      'Recommendation table is empty, please first update the recommendation.')
              return
  
          if self._recommendation_selection.value is not None:
              self._coord.value = self._recommendation_table['y-coord'][self._recommendation_selection.value]
              self._x_coord.value = self._recommendation_table[
                  'x-coord'][self._recommendation_selection.value]
              self._models.value = [
                  self._recommendation_table['model'][self._recommendation_selection.value]]
              self._data.value = [
                  self._recommendation_table['data'][self._recommendation_selection.value]]
              self._plot(None)
  
      def _plot(self, d):
          with self._output:
              clear_output(wait=True)
              display(go.FigureWidget(
                      paiplot.histogram_data_conditional_error(widget_repo.ml_repo,
                                                               self._data_model_selection.get_models(), self._data_model_selection.get_data(),
                                                               x_coordinate=self._x_coord.value,
                                                               y_coordinate=self._coord.value,
                                                               percentile=self._quantile.value/100.0)
                      ))
              self._output_tab.selected_index = 0
  
      def _recommend(self, d):
          self._output_tab.set_title(1, 'computing...')
          self._recommendation_table = pd.DataFrame.from_dict(
              plt_helper.get_ptws_error_dist_mmd(widget_repo.ml_repo, self._data_model_selection.get_models(),
                                                 data=self._data_model_selection.get_data(),
                                                 start_index=0, end_index=-1, percentile=self._quantile.value/100.0,
                                                 scale=self._scale.value,
                                                 cache=self._cache_in_repo,
                                                 metric=self._kernel_selection.value,
                                                 gamma=self._gamma.value)
          )
          self._recommendation_table['model version']
          self._recommendation_table['data version']
          self._recommendation_table.sort_values(
              ['mmd'], ascending=False, inplace=True)
          with self._recommendation_output:
              clear_output(wait=True)
              display(
                  self._recommendation_table.iloc[0:self._max_num_recommendations.value])
          self._output_tab.selected_index = 1
          self._output_tab.set_title(1, 'recommendations')
          self._recommendation_selection.value = self._recommendation_table.index[0]
  
      @_add_title_and_border('Data Distribution of Largest Pointwise Errors.')
      def get_widget(self):
          return widgets.HBox(children=[
              self._accordion,
              self._output_tab
          ])
  
  
  class ScatterModelError:
      def __init__(self):
          self._model_data_selector = _ModelAndDataSelectorWithVersion(
              display_selection=False)
          self._update_button = widgets.Button(description='update')
          self._update_button.on_click(self._plot)
          self._output = widgets.Output()
          self._coord = widgets.Select(
              options=widget_repo.data._y_coord_names,
              value=widget_repo.data._y_coord_names[0],
              disabled=False
          )
          self._x_coord = widgets.Select(
              options=widget_repo.data._x_coord_names,
              value=widget_repo.data._x_coord_names[0],
              disabled=False
          )
  
      def _get_selection_widget(self):
          coordinates = widgets.Accordion(children=[
              widgets.VBox(children=[
                  widgets.Label(value='y-coordinates'),
                  self._coord,
                  widgets.Label(value='x-coordinates'),
                  self._x_coord,
              ]
              )
          ]
          )
          coordinates.set_title(0, 'Coordinates')
          return widgets.VBox(children=[
              self._model_data_selector.get_widget(),
              coordinates,
              self._update_button]
          )
  
      def _plot(self, d):
          with self._output:
              clear_output(wait=True)
              display(go.FigureWidget(
                      paiplot.scatter_model_error(widget_repo.ml_repo,
                                                  self._model_data_selector.get_models(),
                                                  self._model_data_selector.get_data(),
                                                  x_coordinate=self._x_coord.value,
                                                  y_coordinate=self._coord.value)
                      ))
  
      @_add_title_and_border('Scatter Plot Pointwise Errors.')
      def get_widget(self):
          return widgets.HBox(children=[
              self._get_selection_widget(),
              self._output
          ])
  
  
  class IndividualConditionalExpectation:
      """Plots the individual conditional expectation at a certain point.
      """
  
      def __init__(self):
          names = widget_repo.data.get_data_names()
          self._model_data_selection = _ModelAndDataSelectorWithVersion()
          self._update_button = widgets.Button(description='update')
          self._update_button.on_click(self._plot)
          self._output = widgets.Output()
          self._cluster_statistics_output = widgets.Output()
          self._output_tab = widgets.Tab(children=[self._output,
                                                   self._cluster_statistics_output
                                                   ])
          self._output_tab.set_title(0, 'ICE plots')
          self._output_tab.set_title(1, 'clustering')
          self._coord = widgets.Select(
              options=widget_repo.data._y_coord_names,
              value=widget_repo.data._y_coord_names[0],
              disabled=False
          )
          self._x_coord = widgets.Select(
              options=widget_repo.data._x_coord_names,
              value=widget_repo.data._x_coord_names[0],
              disabled=False
          )
          self._x_value_start = widgets.FloatText(value=-1.0)
          self._x_value_end = widgets.FloatText(value=1.0)
          self._n_x_points = widgets.IntText(value=10)
          self._accordion = widgets.Accordion(children=[
              self._get_selection_widget(),
              self._get_clustering_widget()
          ])
  
          self._accordion.set_title(0, 'Selection')
          self._accordion.set_title(1, 'Clustering')
  
      def _get_selection_widget(self):
          return widgets.VBox(children=[
              self._model_data_selection.get_widget(),
              widgets.Label(value='y-coordinates'),
              self._coord,
              widgets.Label(value='x-coordinates'),
              self._x_coord,
              widgets.Label(value='x-start'),
              self._x_value_start,
              widgets.Label(value='x-end'),
              self._x_value_end,
              widgets.Label(value='num x-points'),
              self._n_x_points,
              self._update_button])
  
      def _get_clustering_widget(self):
          self._update_clustering = widgets.Button(description='update')
          self._use_clustering = widgets.Checkbox(
              value=True, description='apply clustering')
          self._max_num_clusters = widgets.IntText(value=20,
                                                   description='maximum number of clusters')
          self._random_state = widgets.IntText(
              value=42, description='Random State')
          self._cache_in_repo = widgets.Checkbox(
              value=True, description='cache ICE in repo')
          self._scale = widgets.Checkbox(
              value=True, description='scale x-values to zero mean and unit variance')
          self._update_clustering.on_click(self._cluster)
  
          return widgets.VBox(children=[
              self._use_clustering,
              self._max_num_clusters,
              self._random_state,
              self._cache_in_repo,
              self._scale
          ])
  
      def _plot(self, d):
          cluster_param = None
          if self._use_clustering.value:
              cluster_param = {'n_clusters': self._max_num_clusters.value,
                               'random_state': self._random_state.value}
           # since the numpy cannot json serialized by default,
           # caching would not working, therefore we convert it into list
          x_points = [x for x in np.linspace(self._x_value_start.value, self._x_value_end.value,
                                             self._n_x_points.value)]
          self._ice = []
          for model, model_versions in self._model_data_selection.get_models().items():
              for data, data_versions in self._model_data_selection.get_data().items():
                  for model_version in model_versions:
                      for data_version in data_versions:
                          self._ice.append((model, model_version, data, data_version,
                                            interpretation.compute_ice(widget_repo.ml_repo,
                                                                       x_points,
                                                                       data,
                                                                       model=model,
                                                                       model_version=model_version,
                                                                       data_version=data_version,
                                                                       y_coordinate=self._coord.value,
                                                                       x_coordinate=self._x_coord.value,
                                                                       cache=self._cache_in_repo.value,
                                                                       clustering_param=cluster_param,
                                                                       end_index=200),
                                            )
                                           )
  
          with self._output:
              clear_output(wait=True)
              display(go.FigureWidget(
                      paiplot.ice(self._ice)
                      ))
              self._output_tab.selected_index = 0
          if len(self._ice) > 0:
              if self._ice[0][-1].cluster_centers is not None:
                  with self._cluster_statistics_output:
                      clear_output(wait=True)
                      display(go.FigureWidget(
                          paiplot.ice_clusters(self._ice)
                      ))
  
      def _cluster(self, d):
  
          self._output_tab.set_title(1, 'computing...')
          models = [x for x in self._models.value]
  
          for x in self._labels.value:
              l = widget_repo.labels[x]
              models.append((l['model'], l['version'],))
  
          with self._cluster_statistics_output:
              clear_output(wait=True)
              # display(self._recommendation_table.iloc[0:self._max_num_recommendations.value])
          self._output_tab.selected_index = 1
          self._output_tab.set_title(1, 'cluster statistics')
          # self._recommendation_selection.value = self._recommendation_table.index[0]
  
      @_add_title_and_border('Individual Conditional Expectation Plots')
      def get_widget(self):
          return widgets.HBox(children=[
              self._accordion,
              self._output_tab
          ])
  
  
  class PlotMeasureVsParameter:
      def __init__(self):
          self._model_selector = widgets.Dropdown(
              options=widget_repo.model.get_models(), value=None)
          self._data_selector = _DataSelectorWithVersion(display_selection=False)
          # self._model_data_selector = _ModelAndDataSelectorWithVersion(
          #    display_selection=False)
          self._measure_selector = widgets.Dropdown(options=widget_repo.measures)
          self._model_selector.observe(
              self._update_param_selector)
          self._param_selector = widgets.Dropdown(options=[])
          self._output = widgets.Output()
          self._update_button = widgets.Button(description='update')
          self._update_button.on_click(self._plot)
  
      def _print(self, message):
          with self._output:
              clear_output(wait=True)
              print(message)
  
      def _update_param_selector(self, change):
          # print(change)
          if self._model_selector.value is None:
              return
          model = self._model_selector.value
          model_param_name = NamingConventions.get_model_param_name(
              model)
          params = []
          try:
              model_params = widget_repo.ml_repo.get(model_param_name)
              for p in model_params.get_params().keys():
                  params.append(p)
          except:
              pass
          train_param_name = str(NamingConventions.TrainingParam(model))
          try:
              train_params = widget_repo.ml_repo.get(train_param_name)
              for p in train_params.get_params().keys():
                  params.append(p)
          except:
              pass
          self._param_selector.options = params
  
      def _plot(self, change):
          measures = []
          model = self._model_selector.value
          if model is None:
              self._print('Please select a model.')
              return
          data = self._data_selector.get_data()
          for d, w in data.items():
              if len(w) > 0:
                  measures.append(str(NamingConventions.Measure(
                      model=NamingConventions.get_model_from_name(model), data=d, measure_type=self._measure_selector.value)))
          if len(measures) == 0:
              self._print('Please select data together with data versions.')
              return
          with self._output:
              clear_output(wait=True)
              # create measure names from selected models, data and measures
              display(go.FigureWidget(
                      paiplot.measure_by_parameter(widget_repo.ml_repo,
                                                   measures, self._param_selector.value)
                      ))
  
      @_add_title_and_border('Measure vs Parameter')
      def get_widget(self):
          return widgets.HBox(children=[
              widgets.VBox(children=[
                  widgets.VBox(children=[
                      widgets.Label(value='Model'),
                      self._model_selector]
                  ),
                  self._data_selector.get_widget(),
                  self._measure_selector,
                  self._param_selector,
                  self._update_button
              ]),
              self._output
          ])
  
apis: numpy.load
apis: matplotlib.pyplot.legend matplotlib.pyplot.figure matplotlib.pyplot.rcdefaults pandas.DataFrame matplotlib.dates.DateFormatter matplotlib.pyplot.subplots pandas.set_option matplotlib.pyplot.show pandas.concat matplotlib.pyplot.plot numpy.where numpy.linspace matplotlib.pyplot.xlabel pandas.DataFrame.from_dict