def read(self, from_item=None, to_item=None,
             from_time=None, to_time=None):
        if to_item is None:
            to_item = self.items.data[-1] if from_item is None else from_item
        if from_item is None:
            from_item = self.items.data[0]
        if not self.items.is_valid_interval(from_item, to_item):
            raise IOError('cannot read items: not a valid interval')
        from_idx = self.items.data.index(from_item)
        to_idx = self.items.data.index(to_item)
        from_pos = self._get_item_position(from_idx)
        to_pos = self._get_item_position(to_idx)
        lower = self._get_from_time(from_time, from_pos)
        upper = self._get_to_time(to_time, to_pos) + 1
        if self.dformat == 'sparse':
            raise NotImplementedError(
                'Reading sparse features not implemented')
        else:
            features = (self.group['features'][:, lower:upper].T
                        if self.version == '0.1'
                        else self.group['features'][lower:upper, ...])
            labels = self._labels_group[lower:upper]
        if to_idx == from_idx:
            features = [features]
            labels = [labels]
        else:
            item_ends = self._index[from_idx:to_idx] - from_pos[0] + 1
            features = np.split(features, item_ends, axis=0)
            labels = np.split(labels, item_ends, axis=0)
        items = self.items.data[from_idx:to_idx + 1]
        if self.properties is None:
            properties = None
        else:
            properties = self.properties[from_idx:to_idx + 1]
        return Data(
            items, labels, features, properties=properties, check=False)