def get_keras_data_sequence_class():
    from keras.utils import Sequence

    class KerasDataSequence(Sequence, ContainerMixin):

        def __init__(self, item_list=None, batch_size=64, buffer_size=None, data_processing_chain=None, meta_processing_chain=None, data_processing_chain_callback_on_epoch_end=None, meta_processing_chain_callback_on_epoch_end=None, transformer_callbacks=None, refresh_buffer_on_epoch=False, data_format='channels_last', target_format='single_target_per_sequence', **kwargs):
            "Constructor\n\n            Parameters\n            ----------\n            item_list : list or dict\n                Items in the data sequence. List containing multi-level dictionary with first level key\n                'data' and 'meta'. Second level should contain parameters for process method in the processing chain.\n                Default value None\n\n            batch_size : int\n                Batch size (item count).\n                Default value 64\n\n            buffer_size : int\n                Internal buffer size (item count). By setting this sufficiently high, data sequence generator can\n                possibly fit all sequence items into internal buffer and can fetch without loading from disk.\n                Set to None, if no internal buffer used.\n                Default value None\n\n            data_processing_chain : ProcessingChain\n                Data processing chain.\n                Default value None\n\n            meta_processing_chain : ProcessingChain\n                Meta processing chain.\n                Default value None\n\n            data_processing_chain_callback_on_epoch_end : list of dict\n                Can be used to call methods with parameters for processing chain at the end of epoch. This can be\n                used to control processing chain's internal status (e.g. roll the data).\n                Default value None\n\n            meta_processing_chain_callback_on_epoch_end : list of dict\n                Can be used to call methods with parameters for processing chain at the end of epoch. This can be\n                used to control processing chain's internal status (e.g. roll the data).\n                Default value None\n\n            transformer_callbacks : list of func\n                Transformer callbacks to jointly process data and meta. This can be used for local data modification and\n                data augmentation.\n                Default value None\n\n            refresh_buffer_on_epoch : bool\n                In case internal data buffer is used, force data and meta refresh at the end of each epoch. Use this if\n                data is modified/augmented differently for each epoch.\n                In case data_processing_chain_callback_on_epoch_end or meta_processing_chain_callback_on_epoch_end is\n                used, this parameter is automatically set to True.\n                Default value False\n\n            data_format : str\n                Keras like data format, controls where channel should be added.\n                Possible values ['channels_first', 'channels_last']\n                Default value 'channels_last'\n\n            target_format : str\n                Meta data interpretation in the relation to the data items.\n                Default value 'single_target_per_segment'\n\n            "
            ContainerMixin.__init__(self, **kwargs)
            self._data_shape = None
            self._data_axis = None
            self.item_list = copy.copy(item_list)
            self.batch_size = batch_size
            self.buffer_size = buffer_size
            self.data_refresh_on_epoch = refresh_buffer_on_epoch
            if (data_format is None):
                data_format = 'channels_last'
            self.data_format = data_format
            if (self.data_format not in ['channels_first', 'channels_last']):
                message = '{name}: Unknown data_format [{data_format}].'.format(name=self.__class__.__name__, data_format=self.data_format)
                self.logger.exception(message)
                raise NotImplementedError(message)
            if (target_format is None):
                target_format = 'single_target_per_sequence'
            self.target_format = target_format
            if (self.target_format not in ['same', 'single_target_per_sequence']):
                message = '{name}: Unknown target_format [{target_format}].'.format(name=self.__class__.__name__, target_format=self.target_format)
                self.logger.exception(message)
                raise NotImplementedError(message)
            if (data_processing_chain_callback_on_epoch_end is None):
                data_processing_chain_callback_on_epoch_end = []
            self.data_processing_chain_callback_on_epoch_end = data_processing_chain_callback_on_epoch_end
            if self.data_processing_chain_callback_on_epoch_end:
                self.data_refresh_on_epoch = True
            if (meta_processing_chain_callback_on_epoch_end is None):
                meta_processing_chain_callback_on_epoch_end = []
            self.meta_processing_chain_callback_on_epoch_end = meta_processing_chain_callback_on_epoch_end
            if (transformer_callbacks is None):
                transformer_callbacks = []
            self.transformer_callbacks = transformer_callbacks
            self.data_processing_chain = data_processing_chain
            self.meta_processing_chain = meta_processing_chain
            if (self.buffer_size is not None):
                self.data_buffer = DataBuffer(size=self.buffer_size)
            else:
                self.data_buffer = None

        def __str__(self):
            ui = FancyStringifier()
            output = ''
            output += (ui.class_name(self.__class__.__name__) + '\n')
            output += (ui.data(indent=2, field='Batch size', value=self.batch_size) + '\n')
            output += (ui.data(indent=2, field='Epoch size', value=len(self), unit='batches') + '\n')
            shape = self.data_shape
            axis = self.data_axis
            output += (ui.data(field='Data item shape', value=shape) + '\n')
            output += (ui.data(indent=4, field='Time', value=shape[axis['time_axis']]) + '\n')
            output += (ui.data(indent=4, field='Data', value=shape[axis['data_axis']]) + '\n')
            if ('sequence_axis' in axis):
                output += (ui.data(indent=4, field='Sequence', value=shape[axis['sequence_axis']]) + '\n')
            output += (ui.data(indent=4, field='Axis', value=axis) + '\n')
            if (self.buffer_size is not None):
                output += (ui.line(field='Buffer') + '\n')
                output += (ui.data(indent=4, field='buffer_size', value=self.buffer_size, unit='items') + '\n')
                output += (ui.data(indent=4, field='buffer usage', value=self.data_buffer.count, unit='items') + '\n')
                output += (ui.data(indent=4, field='buffer usage', value=((self.data_buffer.count / float(self.buffer_size)) * 100), unit='%') + '\n')
            return output

        def __getitem__(self, index):
            start_index = (index * self.batch_size)
            stop_index = ((index + 1) * self.batch_size)
            batch_buffer_data = []
            batch_buffer_meta = []
            for item_index in range(start_index, stop_index):
                if (item_index < len(self.item_list)):
                    item = self.item_list[item_index]
                    (data, meta) = self.process_item(item=item)
                    if self.transformer_callbacks:
                        for callback in self.transformer_callbacks:
                            (data, meta) = callback(data=data, meta=meta)
                    batch_buffer_data.append(data.data)
                    if (self.target_format == 'single_target_per_sequence'):
                        for i in range(0, data.shape[data.sequence_axis]):
                            batch_buffer_meta.append(meta.data[:, 0])
                    elif (self.target_format == 'same'):
                        batch_buffer_meta.append(numpy.repeat(a=meta.data, repeats=data.length, axis=1))
            if (len(data.shape) == 2):
                if (data.time_axis == 0):
                    batch_buffer_data = numpy.vstack(batch_buffer_data)
                elif (data.time_axis == 1):
                    batch_buffer_data = numpy.hstack(batch_buffer_data)
            elif (len(data.shape) == 3):
                if (data.sequence_axis == 0):
                    batch_buffer_data = numpy.vstack(batch_buffer_data)
                elif (data.sequence_axis == 1):
                    batch_buffer_data = numpy.hstack(batch_buffer_data)
                elif (data.sequence_axis == 2):
                    batch_buffer_data = numpy.dstack(batch_buffer_data)
                if (self.data_format == 'channels_first'):
                    batch_buffer_data = numpy.expand_dims(batch_buffer_data, axis=0)
                elif (self.data_format == 'channels_last'):
                    batch_buffer_data = numpy.expand_dims(batch_buffer_data, axis=3)
            if (self.target_format == 'single_target_per_sequence'):
                batch_buffer_meta = numpy.vstack(batch_buffer_meta)
            elif (self.target_format == 'same'):
                batch_buffer_meta = numpy.hstack(batch_buffer_meta).T
            return (batch_buffer_data, batch_buffer_meta)

        def __len__(self):
            num_batches = int(numpy.ceil((len(self.item_list) / float(self.batch_size))))
            if (num_batches > 0):
                return num_batches
            else:
                return 1

        @property
        def data_shape(self):
            if (self._data_shape is None):
                data = self.process_item(item=self.item_list[0])[0]
                self._data_shape = data.shape
                self._data_axis = {'time_axis': data.time_axis, 'data_axis': data.data_axis}
                if hasattr(data, 'sequence_axis'):
                    self._data_axis['sequence_axis'] = data.sequence_axis
            return self._data_shape

        @property
        def data_axis(self):
            if (self._data_axis is None):
                data = self.process_item(item=self.item_list[0])[0]
                self._data_shape = data.shape
                self._data_axis = {'time_axis': data.time_axis, 'data_axis': data.data_axis}
                if hasattr(data, 'sequence_axis'):
                    self._data_axis['sequence_axis'] = data.sequence_axis
            return self._data_axis

        @property
        def data_size(self):
            shape = self.data_shape
            axis = self.data_axis
            size = {'time': shape[axis['time_axis']], 'data': shape[axis['data_axis']]}
            if ('sequence_axis' in axis):
                size['sequence'] = shape[axis['sequence_axis']]
            return size

        def process_item(self, item):
            if (self.data_buffer is not None):
                if (not self.data_buffer.key_exists(key=item)):
                    data = self.data_processing_chain.process(**item['data'])
                    meta = self.meta_processing_chain.process(**item['meta'])
                    self.data_buffer.set(key=item, data=data, meta=meta)
                else:
                    (data, meta) = self.data_buffer.get(key=item)
            else:
                data = self.data_processing_chain.process(**item['data'])
                meta = self.meta_processing_chain.process(**item['meta'])
            return (data, meta)

        def on_epoch_end(self):
            if self.data_processing_chain_callback_on_epoch_end:
                for callback_parameters in self.data_processing_chain_callback_on_epoch_end:
                    if ('method_name' in callback_parameters):
                        self.data_processing_chain.call_method(method_name=callback_parameters['method_name'], parameters=callback_parameters.get('parameters', {}))
            if self.meta_processing_chain_callback_on_epoch_end:
                for callback_parameters in self.meta_processing_chain_callback_on_epoch_end:
                    if ('method_name' in callback_parameters):
                        self.data_processing_chain.call_method(method_name=callback_parameters['method_name'], parameters=callback_parameters.get('parameters', {}))
            if ((self.data_buffer is not None) and self.data_refresh_on_epoch):
                self.data_buffer.clear()
    return KerasDataSequence
def get_keras_data_sequence_class():
    from keras.utils import Sequence

    class KerasDataSequence(Sequence, ContainerMixin):

        def __init__(self, item_list=None, batch_size=64, buffer_size=None, data_processing_chain=None, meta_processing_chain=None, data_processing_chain_callback_on_epoch_end=None, meta_processing_chain_callback_on_epoch_end=None, transformer_callbacks=None, refresh_buffer_on_epoch=False, data_format='channels_last', target_format='single_target_per_sequence', **kwargs):
            "Constructor\n\n            Parameters\n            ----------\n            item_list : list or dict\n                Items in the data sequence. List containing multi-level dictionary with first level key\n                'data' and 'meta'. Second level should contain parameters for process method in the processing chain.\n                Default value None\n\n            batch_size : int\n                Batch size (item count).\n                Default value 64\n\n            buffer_size : int\n                Internal buffer size (item count). By setting this sufficiently high, data sequence generator can\n                possibly fit all sequence items into internal buffer and can fetch without loading from disk.\n                Set to None, if no internal buffer used.\n                Default value None\n\n            data_processing_chain : ProcessingChain\n                Data processing chain.\n                Default value None\n\n            meta_processing_chain : ProcessingChain\n                Meta processing chain.\n                Default value None\n\n            data_processing_chain_callback_on_epoch_end : list of dict\n                Can be used to call methods with parameters for processing chain at the end of epoch. This can be\n                used to control processing chain's internal status (e.g. roll the data).\n                Default value None\n\n            meta_processing_chain_callback_on_epoch_end : list of dict\n                Can be used to call methods with parameters for processing chain at the end of epoch. This can be\n                used to control processing chain's internal status (e.g. roll the data).\n                Default value None\n\n            transformer_callbacks : list of func\n                Transformer callbacks to jointly process data and meta. This can be used for local data modification and\n                data augmentation.\n                Default value None\n\n            refresh_buffer_on_epoch : bool\n                In case internal data buffer is used, force data and meta refresh at the end of each epoch. Use this if\n                data is modified/augmented differently for each epoch.\n                In case data_processing_chain_callback_on_epoch_end or meta_processing_chain_callback_on_epoch_end is\n                used, this parameter is automatically set to True.\n                Default value False\n\n            data_format : str\n                Keras like data format, controls where channel should be added.\n                Possible values ['channels_first', 'channels_last']\n                Default value 'channels_last'\n\n            target_format : str\n                Meta data interpretation in the relation to the data items.\n                Default value 'single_target_per_segment'\n\n            "
            ContainerMixin.__init__(self, **kwargs)
            self._data_shape = None
            self._data_axis = None
            self.item_list = copy.copy(item_list)
            self.batch_size = batch_size
            self.buffer_size = buffer_size
            self.data_refresh_on_epoch = refresh_buffer_on_epoch
            if (data_format is None):
                data_format = 'channels_last'
            self.data_format = data_format
            if (self.data_format not in ['channels_first', 'channels_last']):
                message = '{name}: Unknown data_format [{data_format}].'.format(name=self.__class__.__name__, data_format=self.data_format)
                self.logger.exception(message)
                raise NotImplementedError(message)
            if (target_format is None):
                target_format = 'single_target_per_sequence'
            self.target_format = target_format
            if (self.target_format not in ['same', 'single_target_per_sequence']):
                message = '{name}: Unknown target_format [{target_format}].'.format(name=self.__class__.__name__, target_format=self.target_format)
                self.logger.exception(message)
                raise NotImplementedError(message)
            if (data_processing_chain_callback_on_epoch_end is None):
                data_processing_chain_callback_on_epoch_end = []
            self.data_processing_chain_callback_on_epoch_end = data_processing_chain_callback_on_epoch_end
            if self.data_processing_chain_callback_on_epoch_end:
                self.data_refresh_on_epoch = True
            if (meta_processing_chain_callback_on_epoch_end is None):
                meta_processing_chain_callback_on_epoch_end = []
            self.meta_processing_chain_callback_on_epoch_end = meta_processing_chain_callback_on_epoch_end
            if (transformer_callbacks is None):
                transformer_callbacks = []
            self.transformer_callbacks = transformer_callbacks
            self.data_processing_chain = data_processing_chain
            self.meta_processing_chain = meta_processing_chain
            if (self.buffer_size is not None):
                self.data_buffer = DataBuffer(size=self.buffer_size)
            else:
                self.data_buffer = None

        def __str__(self):
            ui = FancyStringifier()
            output = 
            output += (ui.class_name(self.__class__.__name__) + '\n')
            output += (ui.data(indent=2, field='Batch size', value=self.batch_size) + '\n')
            output += (ui.data(indent=2, field='Epoch size', value=len(self), unit='batches') + '\n')
            shape = self.data_shape
            axis = self.data_axis
            output += (ui.data(field='Data item shape', value=shape) + '\n')
            output += (ui.data(indent=4, field='Time', value=shape[axis['time_axis']]) + '\n')
            output += (ui.data(indent=4, field='Data', value=shape[axis['data_axis']]) + '\n')
            if ('sequence_axis' in axis):
                output += (ui.data(indent=4, field='Sequence', value=shape[axis['sequence_axis']]) + '\n')
            output += (ui.data(indent=4, field='Axis', value=axis) + '\n')
            if (self.buffer_size is not None):
                output += (ui.line(field='Buffer') + '\n')
                output += (ui.data(indent=4, field='buffer_size', value=self.buffer_size, unit='items') + '\n')
                output += (ui.data(indent=4, field='buffer usage', value=self.data_buffer.count, unit='items') + '\n')
                output += (ui.data(indent=4, field='buffer usage', value=((self.data_buffer.count / float(self.buffer_size)) * 100), unit='%') + '\n')
            return output

        def __getitem__(self, index):
            start_index = (index * self.batch_size)
            stop_index = ((index + 1) * self.batch_size)
            batch_buffer_data = []
            batch_buffer_meta = []
            for item_index in range(start_index, stop_index):
                if (item_index < len(self.item_list)):
                    item = self.item_list[item_index]
                    (data, meta) = self.process_item(item=item)
                    if self.transformer_callbacks:
                        for callback in self.transformer_callbacks:
                            (data, meta) = callback(data=data, meta=meta)
                    batch_buffer_data.append(data.data)
                    if (self.target_format == 'single_target_per_sequence'):
                        for i in range(0, data.shape[data.sequence_axis]):
                            batch_buffer_meta.append(meta.data[:, 0])
                    elif (self.target_format == 'same'):
                        batch_buffer_meta.append(numpy.repeat(a=meta.data, repeats=data.length, axis=1))
            if (len(data.shape) == 2):
                if (data.time_axis == 0):
                    batch_buffer_data = numpy.vstack(batch_buffer_data)
                elif (data.time_axis == 1):
                    batch_buffer_data = numpy.hstack(batch_buffer_data)
            elif (len(data.shape) == 3):
                if (data.sequence_axis == 0):
                    batch_buffer_data = numpy.vstack(batch_buffer_data)
                elif (data.sequence_axis == 1):
                    batch_buffer_data = numpy.hstack(batch_buffer_data)
                elif (data.sequence_axis == 2):
                    batch_buffer_data = numpy.dstack(batch_buffer_data)
                if (self.data_format == 'channels_first'):
                    batch_buffer_data = numpy.expand_dims(batch_buffer_data, axis=0)
                elif (self.data_format == 'channels_last'):
                    batch_buffer_data = numpy.expand_dims(batch_buffer_data, axis=3)
            if (self.target_format == 'single_target_per_sequence'):
                batch_buffer_meta = numpy.vstack(batch_buffer_meta)
            elif (self.target_format == 'same'):
                batch_buffer_meta = numpy.hstack(batch_buffer_meta).T
            return (batch_buffer_data, batch_buffer_meta)

        def __len__(self):
            num_batches = int(numpy.ceil((len(self.item_list) / float(self.batch_size))))
            if (num_batches > 0):
                return num_batches
            else:
                return 1

        @property
        def data_shape(self):
            if (self._data_shape is None):
                data = self.process_item(item=self.item_list[0])[0]
                self._data_shape = data.shape
                self._data_axis = {'time_axis': data.time_axis, 'data_axis': data.data_axis}
                if hasattr(data, 'sequence_axis'):
                    self._data_axis['sequence_axis'] = data.sequence_axis
            return self._data_shape

        @property
        def data_axis(self):
            if (self._data_axis is None):
                data = self.process_item(item=self.item_list[0])[0]
                self._data_shape = data.shape
                self._data_axis = {'time_axis': data.time_axis, 'data_axis': data.data_axis}
                if hasattr(data, 'sequence_axis'):
                    self._data_axis['sequence_axis'] = data.sequence_axis
            return self._data_axis

        @property
        def data_size(self):
            shape = self.data_shape
            axis = self.data_axis
            size = {'time': shape[axis['time_axis']], 'data': shape[axis['data_axis']]}
            if ('sequence_axis' in axis):
                size['sequence'] = shape[axis['sequence_axis']]
            return size

        def process_item(self, item):
            if (self.data_buffer is not None):
                if (not self.data_buffer.key_exists(key=item)):
                    data = self.data_processing_chain.process(**item['data'])
                    meta = self.meta_processing_chain.process(**item['meta'])
                    self.data_buffer.set(key=item, data=data, meta=meta)
                else:
                    (data, meta) = self.data_buffer.get(key=item)
            else:
                data = self.data_processing_chain.process(**item['data'])
                meta = self.meta_processing_chain.process(**item['meta'])
            return (data, meta)

        def on_epoch_end(self):
            if self.data_processing_chain_callback_on_epoch_end:
                for callback_parameters in self.data_processing_chain_callback_on_epoch_end:
                    if ('method_name' in callback_parameters):
                        self.data_processing_chain.call_method(method_name=callback_parameters['method_name'], parameters=callback_parameters.get('parameters', {}))
            if self.meta_processing_chain_callback_on_epoch_end:
                for callback_parameters in self.meta_processing_chain_callback_on_epoch_end:
                    if ('method_name' in callback_parameters):
                        self.data_processing_chain.call_method(method_name=callback_parameters['method_name'], parameters=callback_parameters.get('parameters', {}))
            if ((self.data_buffer is not None) and self.data_refresh_on_epoch):
                self.data_buffer.clear()
    return KerasDataSequence