<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def clustermap( adata, obs_keys=None, use_raw=None, show=None, save=None, **kwds):
 """\ Hierarchically-clustered heatmap. Wraps `seaborn.clustermap <https://seaborn.pydata.org/generated/seaborn.clustermap.html>`__ for :class:`~anndata.AnnData`. Parameters adata : :class:`~anndata.AnnData` Annotated data matrix. obs_keys : `str` Categorical annotation to plot with a different color map. Currently, only a single key is supported. use_raw : `bool`, optional (default: `None`) Use `raw` attribute of `adata` if present. {show_save_ax} **kwds : keyword arguments Keyword arguments passed to `seaborn.clustermap <https://seaborn.pydata.org/generated/seaborn.clustermap.html>`__. Returns ------- If `show == False`, a `seaborn.ClusterGrid` object. Notes ----- The returned object has a savefig() method that should be used if you want to save the figure object without clipping the dendrograms. To access the reordered row indices, use: clustergrid.dendrogram_row.reordered_ind Column indices, use: clustergrid.dendrogram_col.reordered_ind Examples -------- Soon to come with figures. In the meanwile, see https://seaborn.pydata.org/generated/seaborn.clustermap.html. """

    if not isinstance(obs_keys, (str, type(None))):
        raise ValueError('Currently, only a single key is supported.')
    sanitize_anndata(adata)
    if use_raw is None and adata.raw is not None: use_raw = True
    X = adata.raw.X if use_raw else adata.X
    if issparse(X):
        X = X.toarray()
    df = pd.DataFrame(X, index=adata.obs_names, columns=adata.var_names)
    if obs_keys is not None:
        row_colors = adata.obs[obs_keys]
        utils.add_colors_for_categorical_sample_annotation(adata, obs_keys)
        # do this more efficiently... just a quick solution
        lut = dict(zip(
            row_colors.cat.categories,
            adata.uns[obs_keys + '_colors']))
        row_colors = adata.obs[obs_keys].map(lut)
        g = sns.clustermap(df, row_colors=row_colors, **kwds)
    else:
        g = sns.clustermap(df, **kwds)
    show = settings.autoshow if show is None else show
    if show: pl.show()
    else: return g