# Path: scib/metrics/clustering.py
# def opt_louvain(adata, label_key, cluster_key, function=None, resolutions=None,
#                 use_rep=None,
#                 inplace=True, plot=False, force=True, verbose=True, **kwargs):
#     """
#     params:
#         label_key: name of column in adata.obs containing biological labels to be
#             optimised against
#         cluster_key: name of column to be added to adata.obs during clustering. 
#             Will be overwritten if exists and `force=True`
#         function: function that computes the cost to be optimised over. Must take as
#             arguments (adata, group1, group2, **kwargs) and returns a number for maximising
#         resolutions: list if resolutions to be optimised over. If `resolutions=None`,
#             default resolutions of 20 values ranging between 0.1 and 2 will be used
#         use_rep: key of embedding to use only if adata.uns['neighbors'] is not defined,
#             otherwise will be ignored
#     returns:
#         res_max: resolution of maximum score
#         score_max: maximum score
#         score_all: `pd.DataFrame` containing all scores at resolutions. Can be used to plot the score profile.
#         clustering: only if `inplace=False`, return cluster assignment as `pd.Series`
#         plot: if `plot=True` plot the score profile over resolution
#     """
# 
#     if verbose:
#         print('Clustering...')
# 
#     if function is None:
#         function = nmi
# 
#     if cluster_key in adata.obs.columns:
#         if force:
#             print(f"Warning: cluster key {cluster_key} already exists "
#                   "in adata.obs and will be overwritten")
#         else:
#             raise ValueError(f"cluster key {cluster_key} already exists in " +
#                              "adata, please remove the key or choose a different name." +
#                              "If you want to force overwriting the key, specify `force=True`")
# 
#     if resolutions is None:
#         n = 20
#         resolutions = [2 * x / n for x in range(1, n + 1)]
# 
#     score_max = 0
#     res_max = resolutions[0]
#     clustering = None
#     score_all = []
# 
#     try:
#         adata.uns['neighbors']
#     except KeyError:
#         if verbose:
#             print('computing neighbours for opt_cluster')
#         sc.pp.neighbors(adata, use_rep=use_rep)
# 
#     for res in resolutions:
#         sc.tl.louvain(adata, resolution=res, key_added=cluster_key)
#         score = function(adata, label_key, cluster_key, **kwargs)
#         if verbose:
#             print(f'resolution: {res}, {function.__name__}: {score}')
#         score_all.append(score)
#         if score_max < score:
#             score_max = score
#             res_max = res
#             clustering = adata.obs[cluster_key]
#         del adata.obs[cluster_key]
# 
#     if verbose:
#         print(f'optimised clustering against {label_key}')
#         print(f'optimal cluster resolution: {res_max}')
#         print(f'optimal score: {score_max}')
# 
#     score_all = pd.DataFrame(zip(resolutions, score_all), columns=('resolution', 'score'))
#     if plot:
#         # score vs. resolution profile
#         sns.lineplot(data=score_all, x='resolution', y='score').set_title('Optimal cluster resolution profile')
#         plt.show()
# 
#     if inplace:
#         adata.obs[cluster_key] = clustering
#         return res_max, score_max, score_all
#     else:
#         return res_max, score_max, score_all, clustering
# 
# Path: scib/metrics/silhouette.py
# def silhouette(
#         adata,
#         group_key,
#         embed,
#         metric='euclidean',
#         scale=True
# ):
#     """
#     Wrapper for sklearn silhouette function values range from [-1, 1] with
#         1 being an ideal fit
#         0 indicating overlapping clusters and
#         -1 indicating misclassified cells
#     By default, the score is scaled between 0 and 1. This is controlled `scale=True`
# 
#     :param group_key: key in adata.obs of cell labels
#     :param embed: embedding key in adata.obsm, default: 'X_pca'
#     :param scale: default True, scale between 0 (worst) and 1 (best)
#     """
#     if embed not in adata.obsm.keys():
#         print(adata.obsm.keys())
#         raise KeyError(f'{embed} not in obsm')
#     asw = silhouette_score(
#         X=adata.obsm[embed],
#         labels=adata.obs[group_key],
#         metric=metric
#     )
#     if scale:
#         asw = (asw + 1) / 2
#     return asw


import pandas as pd
from sklearn.metrics import f1_score
from .clustering import opt_louvain
from .silhouette import silhouette
        obs = adata.obs
        max_cluster = None
        max_f1 = 0
        for cluster in obs[cluster_key].unique():
            y_pred = obs[cluster_key] == cluster
            y_true = obs[label_key] == label
            f1 = f1_score(y_pred, y_true)
            if f1 > max_f1:
                max_f1 = f1
                max_cluster = cluster
        if argmax:
            return max_cluster
        return max_f1

    if cluster:
        # F1-score on clustering
        opt_louvain(
            adata_tmp,
            label_key,
            cluster_key=iso_label_key,
            label=isolated_label,
            use_rep=embed,
            function=max_f1,
            verbose=False,
            inplace=True
        )
        score = max_f1(adata_tmp, label_key, iso_label_key, isolated_label, argmax=False)
    else:
        # AWS score between label
        adata_tmp.obs[iso_label_key] = adata_tmp.obs[label_key] == isolated_label

# Path: scib/metrics/clustering.py
# def opt_louvain(adata, label_key, cluster_key, function=None, resolutions=None,
#                 use_rep=None,
#                 inplace=True, plot=False, force=True, verbose=True, **kwargs):
#     """
#     params:
#         label_key: name of column in adata.obs containing biological labels to be
#             optimised against
#         cluster_key: name of column to be added to adata.obs during clustering. 
#             Will be overwritten if exists and `force=True`
#         function: function that computes the cost to be optimised over. Must take as
#             arguments (adata, group1, group2, **kwargs) and returns a number for maximising
#         resolutions: list if resolutions to be optimised over. If `resolutions=None`,
#             default resolutions of 20 values ranging between 0.1 and 2 will be used
#         use_rep: key of embedding to use only if adata.uns['neighbors'] is not defined,
#             otherwise will be ignored
#     returns:
#         res_max: resolution of maximum score
#         score_max: maximum score
#         score_all: `pd.DataFrame` containing all scores at resolutions. Can be used to plot the score profile.
#         clustering: only if `inplace=False`, return cluster assignment as `pd.Series`
#         plot: if `plot=True` plot the score profile over resolution
#     """
# 
#     if verbose:
#         print('Clustering...')
# 
#     if function is None:
#         function = nmi
# 
#     if cluster_key in adata.obs.columns:
#         if force:
#             print(f"Warning: cluster key {cluster_key} already exists "
#                   "in adata.obs and will be overwritten")
#         else:
#             raise ValueError(f"cluster key {cluster_key} already exists in " +
#                              "adata, please remove the key or choose a different name." +
#                              "If you want to force overwriting the key, specify `force=True`")
# 
#     if resolutions is None:
#         n = 20
#         resolutions = [2 * x / n for x in range(1, n + 1)]
# 
#     score_max = 0
#     res_max = resolutions[0]
#     clustering = None
#     score_all = []
# 
#     try:
#         adata.uns['neighbors']
#     except KeyError:
#         if verbose:
#             print('computing neighbours for opt_cluster')
#         sc.pp.neighbors(adata, use_rep=use_rep)
# 
#     for res in resolutions:
#         sc.tl.louvain(adata, resolution=res, key_added=cluster_key)
#         score = function(adata, label_key, cluster_key, **kwargs)
#         if verbose:
#             print(f'resolution: {res}, {function.__name__}: {score}')
#         score_all.append(score)
#         if score_max < score:
#             score_max = score
#             res_max = res
#             clustering = adata.obs[cluster_key]
#         del adata.obs[cluster_key]
# 
#     if verbose:
#         print(f'optimised clustering against {label_key}')
#         print(f'optimal cluster resolution: {res_max}')
#         print(f'optimal score: {score_max}')
# 
#     score_all = pd.DataFrame(zip(resolutions, score_all), columns=('resolution', 'score'))
#     if plot:
#         # score vs. resolution profile
#         sns.lineplot(data=score_all, x='resolution', y='score').set_title('Optimal cluster resolution profile')
#         plt.show()
# 
#     if inplace:
#         adata.obs[cluster_key] = clustering
#         return res_max, score_max, score_all
#     else:
#         return res_max, score_max, score_all, clustering
# 
# Path: scib/metrics/silhouette.py
# def silhouette(
#         adata,
#         group_key,
#         embed,
#         metric='euclidean',
#         scale=True
# ):
#     """
#     Wrapper for sklearn silhouette function values range from [-1, 1] with
#         1 being an ideal fit
#         0 indicating overlapping clusters and
#         -1 indicating misclassified cells
#     By default, the score is scaled between 0 and 1. This is controlled `scale=True`
# 
#     :param group_key: key in adata.obs of cell labels
#     :param embed: embedding key in adata.obsm, default: 'X_pca'
#     :param scale: default True, scale between 0 (worst) and 1 (best)
#     """
#     if embed not in adata.obsm.keys():
#         print(adata.obsm.keys())
#         raise KeyError(f'{embed} not in obsm')
#     asw = silhouette_score(
#         X=adata.obsm[embed],
#         labels=adata.obs[group_key],
#         metric=metric
#     )
#     if scale:
#         asw = (asw + 1) / 2
#     return asw


# Path: scib/metrics/isolated_labels.py
import pandas as pd
from sklearn.metrics import f1_score
from .clustering import opt_louvain
from .silhouette import silhouette

        obs = adata.obs
        max_cluster = None
        max_f1 = 0
        for cluster in obs[cluster_key].unique():
            y_pred = obs[cluster_key] == cluster
            y_true = obs[label_key] == label
            f1 = f1_score(y_pred, y_true)
            if f1 > max_f1:
                max_f1 = f1
                max_cluster = cluster
        if argmax:
            return max_cluster
        return max_f1

    if cluster:
        # F1-score on clustering
        opt_louvain(
            adata_tmp,
            label_key,
            cluster_key=iso_label_key,
            label=isolated_label,
            use_rep=embed,
            function=max_f1,
            verbose=False,
            inplace=True
        )
        score = max_f1(adata_tmp, label_key, iso_label_key, isolated_label, argmax=False)
    else:
        # AWS score between label
        adata_tmp.obs[iso_label_key] = adata_tmp.obs[label_key] == isolated_label

        score = silhouette(adata_tmp, iso_label_key, embed)