Unpacks DynamicMap into container of DynamicMaps

        Collation allows unpacking DynamicMaps which return Layout,
        NdLayout or GridSpace objects into a single such object
        containing DynamicMaps. Assumes that the items in the layout
        or grid that is returned do not change.

        Returns:
            Collated container containing DynamicMaps

def collate(self):
        """Unpacks DynamicMap into container of DynamicMaps

        Collation allows unpacking DynamicMaps which return Layout,
        NdLayout or GridSpace objects into a single such object
        containing DynamicMaps. Assumes that the items in the layout
        or grid that is returned do not change.

        Returns:
            Collated container containing DynamicMaps
        """
        # Initialize
        if self.last is not None:
            initialized = self
        else:
            initialized = self.clone()
            initialized[initialized._initial_key()]

        if not isinstance(initialized.last, (Layout, NdLayout, GridSpace)):
            return self

        container = initialized.last.clone(shared_data=False)
        type_counter = defaultdict(int)

        # Get stream mapping from callback
        remapped_streams = []
        self_dstreams = util.dimensioned_streams(self)
        streams = self.callback.stream_mapping
        for i, (k, v) in enumerate(initialized.last.data.items()):
            vstreams = streams.get(i, [])
            if not vstreams:
                if isinstance(initialized.last, Layout):
                    for l in range(len(k)):
                        path = '.'.join(k[:l])
                        if path in streams:
                            vstreams = streams[path]
                            break
                else:
                    vstreams = streams.get(k, [])
            if any(s in remapped_streams for s in vstreams):
                raise ValueError(
                    "The stream_mapping supplied on the Callable "
                    "is ambiguous please supply more specific Layout "
                    "path specs.")
            remapped_streams += vstreams

            # Define collation callback
            def collation_cb(*args, **kwargs):
                layout = self[args]
                layout_type = type(layout).__name__
                if len(container.keys()) != len(layout.keys()):
                    raise ValueError('Collated DynamicMaps must return '
                                     '%s with consistent number of items.'
                                     % layout_type)

                key = kwargs['selection_key']
                index = kwargs['selection_index']
                obj_type = kwargs['selection_type']
                dyn_type_map = defaultdict(list)
                for k, v in layout.data.items():
                    if k == key:
                        return layout[k]
                    dyn_type_map[type(v)].append(v)

                dyn_type_counter = {t: len(vals) for t, vals in dyn_type_map.items()}
                if dyn_type_counter != type_counter:
                    raise ValueError('The objects in a %s returned by a '
                                     'DynamicMap must consistently return '
                                     'the same number of items of the '
                                     'same type.' % layout_type)
                return dyn_type_map[obj_type][index]

            callback = Callable(partial(collation_cb, selection_key=k,
                                        selection_index=type_counter[type(v)],
                                        selection_type=type(v)),
                                inputs=[self])
            vstreams = list(util.unique_iterator(self_dstreams + vstreams))
            vdmap = self.clone(callback=callback, shared_data=False,
                               streams=vstreams)
            type_counter[type(v)] += 1

            # Remap source of streams
            for stream in vstreams:
                if stream.source is self:
                    stream.source = vdmap
            container[k] = vdmap

        unmapped_streams = [repr(stream) for stream in self.streams
                            if (stream.source is self) and
                            (stream not in remapped_streams)
                            and stream.linked]
        if unmapped_streams:
            raise ValueError(
                'The following streams are set to be automatically '
                'linked to a plot, but no stream_mapping specifying '
                'which item in the (Nd)Layout to link it to was found:\n%s'
                % ', '.join(unmapped_streams)
            )
        return container