<filename>scprep/run/slingshot.py<gh_stars>10-100
from .. import utils
from . import r_function

import numpy as np
import pandas as pd
import warnings


def install(site_repository=None, update=False, version=None, verbose=True):
    """Install the required R packages to run Slingshot.

    Parameters
    ----------
    site_repository : string, optional (default: None)
        additional repository in which to look for packages to install.
        This repository will be prepended to the default repositories
    update : boolean, optional (default: False)
        When False, don't attempt to update old packages.
        When True, update old packages automatically.
    version : string, optional (default: None)
        Bioconductor version to install, e.g., version = "3.8".
        The special symbol version = "devel" installs the current 'development' version.
        If None, installs from the current version.
    verbose : boolean, optional (default: True)
        Install script verbosity.
    """
    r_function.install_bioconductor(
        "slingshot",
        site_repository=site_repository,
        update=update,
        version=version,
        verbose=verbose,
    )


_Slingshot = r_function.RFunction(
    setup="""
        library(slingshot)
    """,
    args="""
        data, cluster_labels,
        start_cluster = NULL, end_cluster = NULL,
        distance = NULL, omega = NULL, lineages = list(), shrink = TRUE,
        extend = "y", reweight = TRUE, reassign = TRUE, thresh = 0.001,
        max_iter = 15, stretch = 2,
        smoother = "smooth.spline", shrink_method = "cosine",
        allow_breaks = TRUE, seed = NULL, ...
    """,
    body="""
        set.seed(seed)
        data <- as.matrix(data)
        cluster_labels <- as.factor(cluster_labels)

        # Run Slingshot
        sling <- slingshot(data, clusterLabels = cluster_labels,
                         start.clus = start_cluster, end.clus = end_cluster,
                         dist.fun = distance, omega = omega, lineages = lineages,
                         shrink = shrink, extend = extend, reweight = reweight,
                         reassign = reassign, thresh = thresh,
                         maxit = max_iter, stretch = stretch,
                         smoother = smoother, shrink.method = shrink_method,
                         allow.breaks = allow_breaks, ...)
        list(pseudotime = slingPseudotime(sling),
             curves = lapply(sling@curves, function(curve) curve$s[curve$ord,]))
    """,
)


def Slingshot(
    data,
    cluster_labels,
    start_cluster=None,
    end_cluster=None,
    distance=None,
    omega=None,
    shrink=True,
    extend="y",
    reweight=True,
    reassign=True,
    thresh=0.001,
    max_iter=15,
    stretch=2,
    smoother="smooth.spline",
    shrink_method="cosine",
    allow_breaks=True,
    seed=None,
    verbose=1,
    **kwargs,
):
    """Perform lineage inference with Slingshot.

    Given a reduced-dimensional data matrix n by p and a vector of cluster labels
    (or matrix of soft cluster assignments, potentially including a -1 label for
    "unclustered"), this function performs lineage inference using a cluster-based
    minimum spanning tree and constructing simulatenous principal curves for branching
    paths through the tree.

    For more details, read about Slingshot on GitHub_ and Bioconductor_.

    .. _GitHub: https://github.com/kstreet13/slingshot
    .. _Bioconductor: https://bioconductor.org/packages/release/bioc/html/slingshot.html

    Parameters
    ----------
    data : array-like, shape=[n_samples, n_dimensions]
        matrix of (reduced dimension) coordinates
        to be used for lineage inference.
    cluster_labels : list-like, shape=[n_samples]
        a vector of cluster labels, optionally including -1's for "unclustered."
    start_cluster : string, optional (default: None)
        indicates the cluster(s) of origin.
        Lineages will be represented by paths coming out of this cluster.
    end_cluster : string, optional (default: None)
        indicates the cluster(s) which will be forced leaf nodes.
        This introduces a constraint on the MST algorithm.
    distance : callable, optional (default: None)
        method for calculating distances between clusters.
        Must take two matrices as input, corresponding to subsets of reduced_dim.
        If the minimum cluster size is larger than the number dimensions,
        the default is to use the joint covariance matrix to find squared distance
        between cluster centers. If not, the default is to use the diagonal of the
        joint covariance matrix. Not currently implemented
    omega : float, optional (default: None)
        this granularity parameter determines the distance between every
        real cluster and the artificial cluster.
        It is parameterized such that this distance is omega / 2,
        making omega the maximum distance between two connected clusters.
        By default, omega = Inf.
    shrink : boolean or float, optional (default: True)
        boolean or numeric between 0 and 1, determines whether and how much to shrink
        branching lineages toward their average prior to the split.
    extend : {'y', 'n', 'pc1'}, optional (default: "y")
        how to handle root and leaf clusters of lineages when
        constructing the initial, piece-wise linear curve.
    reweight : boolean, optional (default: True)
        whether to allow cells shared between lineages to be reweighted during
        curve-fitting. If True, cells shared between lineages will be iteratively
        reweighted based on the quantiles of their projection distances to each curve.
    reassign : boolean, optional (default: True)
        whether to reassign cells to lineages at each iteration.
        If True, cells will be added to a lineage when their
        projection distance to the curve is less than the median
        distance for all cells currently assigned to the lineage.
        Additionally, shared cells will be removed from a lineage if
        their projection distance to the curve is above the 90th
        percentile and their weight along the curve is less than 0.1.
    thresh : float, optional (default: 0.001)
        determines the convergence criterion. Percent change in the
        total distance from cells to their projections along curves
        must be less than thresh.
    max_iter : int, optional (default: 15)
        maximum number of iterations
    stretch : int, optional (default: 2)
        factor between 0 and 2 by which curves can be extrapolated beyond endpoints
    smoother : {"smooth.spline", "lowess", "periodic_lowess"},
        optional (default: "smooth.spline")
        choice of smoother. "periodic_lowess" allows one to fit closed
        curves. Beware, you may want to use iter = 0 with "lowess".
    shrink_method : string, optional (default: "cosine")
        how to determine the appropriate amount of shrinkage for a
        branching lineage. Accepted values: "gaussian", "rectangular",
        "triangular", "epanechnikov", "biweight", "triweight",
        "cosine", "optcosine", "density".
    allow_breaks : boolean, optional (default: True)
        determines whether curves that branch very close to the origin
        should be allowed to have different starting points.
    seed : int or None, optional (default: None)
        Seed to use for generating random numbers.
    verbose : int, optional (default: 1)
        Logging verbosity between 0 and 2.

    Returns
    -------
    slingshot : dict
        Contains the following keys:
    pseudotime : array-like, shape=[n_samples, n_curves]
        Pseudotime projection of each cell onto each principal curve.
        Value is `np.nan` if the cell does not lie on the curve
    branch : list-like, shape=[n_samples]
        Branch assignment for each cell
    curves : array_like, shape=[n_curves, n_samples, n_dimensions]
        Coordinates of each principle curve in the reduced dimension

    Examples
    --------
    >>> import scprep
    >>> import phate
    >>> data, clusters = phate.tree.gen_dla(n_branch=4, n_dim=200, branch_length=200)
    >>> phate_op = phate.PHATE()
    >>> data_phate = phate_op.fit_transform(data)
    >>> slingshot = scprep.run.Slingshot(data_phate, clusters)
    >>> ax = scprep.plot.scatter2d(
    ...     data_phate,
    ...     c=slingshot['pseudotime'][:,0],
    ...     cmap='magma',
    ...     legend_title='Branch 1'
    ... )
    >>> scprep.plot.scatter2d(
    ...     data_phate,
    ...     c=slingshot['pseudotime'][:,1],
    ...     cmap='viridis',
    ...     ax=ax,
    ...     ticks=False,
    ...     label_prefix='PHATE',
    ...     legend_title='Branch 2'
    ...     )
    >>> for curve in slingshot['curves']:
    ...     ax.plot(curve[:,0], curve[:,1], c='black')
    >>> ax = scprep.plot.scatter2d(data_phate, c=slingshot['branch'],
    ...                        legend_title='Branch', ticks=False, label_prefix='PHATE')
    >>> for curve in slingshot['curves']:
    ...     ax.plot(curve[:,0], curve[:,1], c='black')
    """
    if seed is None:
        seed = np.random.randint(2 ** 16 - 1)
    if distance is not None:
        raise NotImplementedError("distance argument not currently implemented")
    np.random.seed(seed)

    index = data.index if isinstance(data, pd.DataFrame) else None

    data = utils.toarray(data)
    if data.shape[1] > 3:
        warnings.warn(
            "Expected data to be low-dimensional. "
            "Got data.shape[1] = {}".format(data.shape[1]),
            UserWarning,
        )
    cluster_labels = utils.toarray(cluster_labels).flatten()
    if not cluster_labels.shape[0] == data.shape[0]:
        raise ValueError(
            "Expected len(cluster_labels) ({}) to equal "
            "data.shape[0] ({})".format(cluster_labels.shape[0], data.shape[0])
        )

    if start_cluster is not None:
        kwargs["start_cluster"] = start_cluster
    if end_cluster is not None:
        kwargs["end_cluster"] = end_cluster
    if omega is not None:
        kwargs["omega"] = omega

    slingshot = _Slingshot(
        data=data,
        cluster_labels=cluster_labels,
        shrink=shrink,
        extend=extend,
        reweight=reweight,
        reassign=reassign,
        thresh=thresh,
        max_iter=max_iter,
        stretch=stretch,
        smoother=smoother,
        shrink_method=shrink_method,
        allow_breaks=allow_breaks,
        **kwargs,
        seed=seed,
        rpy_verbose=verbose,
    )
    slingshot["curves"] = np.array(list(slingshot["curves"].values()))

    membership = (~np.isnan(slingshot["pseudotime"])).astype(int)
    branch = np.sum(membership * (2 ** np.arange(membership.shape[1])), axis=1)
    # reorder based on pseudotime
    branch_ids = np.unique(branch)
    branch_means = [
        np.nanmean(slingshot["pseudotime"][branch == id])
        if not np.all(np.isnan(slingshot["pseudotime"][branch == id]))
        else np.nan
        for id in branch_ids
    ]
    branch_order = np.argsort(branch_means)
    branch_old = branch.copy()
    for i in range(len(branch_order)):
        j = branch_order[i]
        if np.isnan(branch_means[j]):
            branch[branch_old == branch_ids[j]] = -1
        else:
            branch[branch_old == branch_ids[j]] = i
    slingshot["branch"] = branch

    if index is not None:
        slingshot["pseudotime"] = pd.DataFrame(slingshot["pseudotime"], index=index)
        slingshot["branch"] = pd.Series(slingshot["branch"], name="branch", index=index)
    return slingshot

<filename>scprep/run/slingshot.py<gh_stars>10-100
from .. import utils
from . import r_function

import numpy as np
import pandas as pd
import warnings


def install(site_repository=None, update=False, version=None, verbose=True):
    """Install the required R packages to run Slingshot.

    Parameters
    ----------
    site_repository : string, optional (default: None)
        additional repository in which to look for packages to install.
        This repository will be prepended to the default repositories
    update : boolean, optional (default: False)
        When False, don't attempt to update old packages.
        When True, update old packages automatically.
    version : string, optional (default: None)
        Bioconductor version to install, e.g., version = "3.8".
        The special symbol version = "devel" installs the current 'development' version.
        If None, installs from the current version.
    verbose : boolean, optional (default: True)
        Install script verbosity.
    """
    r_function.install_bioconductor(
        "slingshot",
        site_repository=site_repository,
        update=update,
        version=version,
        verbose=verbose,
    )


_Slingshot = r_function.RFunction(
    setup="""
        library(slingshot)
    """,
    args="""
        data, cluster_labels,
        start_cluster = NULL, end_cluster = NULL,
        distance = NULL, omega = NULL, lineages = list(), shrink = TRUE,
        extend = "y", reweight = TRUE, reassign = TRUE, thresh = 0.001,
        max_iter = 15, stretch = 2,
        smoother = "smooth.spline", shrink_method = "cosine",
        allow_breaks = TRUE, seed = NULL, ...
    """,
    body="""
        set.seed(seed)
        data <- as.matrix(data)
        cluster_labels <- as.factor(cluster_labels)

        # Run Slingshot
        sling <- slingshot(data, clusterLabels = cluster_labels,
                         start.clus = start_cluster, end.clus = end_cluster,
                         dist.fun = distance, omega = omega, lineages = lineages,
                         shrink = shrink, extend = extend, reweight = reweight,
                         reassign = reassign, thresh = thresh,
                         maxit = max_iter, stretch = stretch,
                         smoother = smoother, shrink.method = shrink_method,
                         allow.breaks = allow_breaks, ...)
        list(pseudotime = slingPseudotime(sling),
             curves = lapply(sling@curves, function(curve) curve$s[curve$ord,]))
    """,
)


def Slingshot(
    data,
    cluster_labels,
    start_cluster=None,
    end_cluster=None,
    distance=None,
    omega=None,
    shrink=True,
    extend="y",
    reweight=True,
    reassign=True,
    thresh=0.001,
    max_iter=15,
    stretch=2,
    smoother="smooth.spline",
    shrink_method="cosine",
    allow_breaks=True,
    seed=None,
    verbose=1,
    **kwargs,
):
    """Perform lineage inference with Slingshot.

    Given a reduced-dimensional data matrix n by p and a vector of cluster labels
    (or matrix of soft cluster assignments, potentially including a -1 label for
    "unclustered"), this function performs lineage inference using a cluster-based
    minimum spanning tree and constructing simulatenous principal curves for branching
    paths through the tree.

    For more details, read about Slingshot on GitHub_ and Bioconductor_.

    .. _GitHub: https://github.com/kstreet13/slingshot
    .. _Bioconductor: https://bioconductor.org/packages/release/bioc/html/slingshot.html

    Parameters
    ----------
    data : array-like, shape=[n_samples, n_dimensions]
        matrix of (reduced dimension) coordinates
        to be used for lineage inference.
    cluster_labels : list-like, shape=[n_samples]
        a vector of cluster labels, optionally including -1's for "unclustered."
    start_cluster : string, optional (default: None)
        indicates the cluster(s) of origin.
        Lineages will be represented by paths coming out of this cluster.
    end_cluster : string, optional (default: None)
        indicates the cluster(s) which will be forced leaf nodes.
        This introduces a constraint on the MST algorithm.
    distance : callable, optional (default: None)
        method for calculating distances between clusters.
        Must take two matrices as input, corresponding to subsets of reduced_dim.
        If the minimum cluster size is larger than the number dimensions,
        the default is to use the joint covariance matrix to find squared distance
        between cluster centers. If not, the default is to use the diagonal of the
        joint covariance matrix. Not currently implemented
    omega : float, optional (default: None)
        this granularity parameter determines the distance between every
        real cluster and the artificial cluster.
        It is parameterized such that this distance is omega / 2,
        making omega the maximum distance between two connected clusters.
        By default, omega = Inf.
    shrink : boolean or float, optional (default: True)
        boolean or numeric between 0 and 1, determines whether and how much to shrink
        branching lineages toward their average prior to the split.
    extend : {'y', 'n', 'pc1'}, optional (default: "y")
        how to handle root and leaf clusters of lineages when
        constructing the initial, piece-wise linear curve.
    reweight : boolean, optional (default: True)
        whether to allow cells shared between lineages to be reweighted during
        curve-fitting. If True, cells shared between lineages will be iteratively
        reweighted based on the quantiles of their projection distances to each curve.
    reassign : boolean, optional (default: True)
        whether to reassign cells to lineages at each iteration.
        If True, cells will be added to a lineage when their
        projection distance to the curve is less than the median
        distance for all cells currently assigned to the lineage.
        Additionally, shared cells will be removed from a lineage if
        their projection distance to the curve is above the 90th
        percentile and their weight along the curve is less than 0.1.
    thresh : float, optional (default: 0.001)
        determines the convergence criterion. Percent change in the
        total distance from cells to their projections along curves
        must be less than thresh.
    max_iter : int, optional (default: 15)
        maximum number of iterations
    stretch : int, optional (default: 2)
        factor between 0 and 2 by which curves can be extrapolated beyond endpoints
    smoother : {"smooth.spline", "lowess", "periodic_lowess"},
        optional (default: "smooth.spline")
        choice of smoother. "periodic_lowess" allows one to fit closed
        curves. Beware, you may want to use iter = 0 with "lowess".
    shrink_method : string, optional (default: "cosine")
        how to determine the appropriate amount of shrinkage for a
        branching lineage. Accepted values: "gaussian", "rectangular",
        "triangular", "epanechnikov", "biweight", "triweight",
        "cosine", "optcosine", "density".
    allow_breaks : boolean, optional (default: True)
        determines whether curves that branch very close to the origin
        should be allowed to have different starting points.
    seed : int or None, optional (default: None)
        Seed to use for generating random numbers.
    verbose : int, optional (default: 1)
        Logging verbosity between 0 and 2.

    Returns
    -------
    slingshot : dict
        Contains the following keys:
    pseudotime : array-like, shape=[n_samples, n_curves]
        Pseudotime projection of each cell onto each principal curve.
        Value is `np.nan` if the cell does not lie on the curve
    branch : list-like, shape=[n_samples]
        Branch assignment for each cell
    curves : array_like, shape=[n_curves, n_samples, n_dimensions]
        Coordinates of each principle curve in the reduced dimension

    Examples
    --------
    >>> import scprep
    >>> import phate
    >>> data, clusters = phate.tree.gen_dla(n_branch=4, n_dim=200, branch_length=200)
    >>> phate_op = phate.PHATE()
    >>> data_phate = phate_op.fit_transform(data)
    >>> slingshot = scprep.run.Slingshot(data_phate, clusters)
    >>> ax = scprep.plot.scatter2d(
    ...     data_phate,
    ...     c=slingshot['pseudotime'][:,0],
    ...     cmap='magma',
    ...     legend_title='Branch 1'
    ... )
    >>> scprep.plot.scatter2d(
    ...     data_phate,
    ...     c=slingshot['pseudotime'][:,1],
    ...     cmap='viridis',
    ...     ax=ax,
    ...     ticks=False,
    ...     label_prefix='PHATE',
    ...     legend_title='Branch 2'
    ...     )
    >>> for curve in slingshot['curves']:
    ...     ax.plot(curve[:,0], curve[:,1], c='black')
    >>> ax = scprep.plot.scatter2d(data_phate, c=slingshot['branch'],
    ...                        legend_title='Branch', ticks=False, label_prefix='PHATE')
    >>> for curve in slingshot['curves']:
    ...     ax.plot(curve[:,0], curve[:,1], c='black')
    """
    if seed is None:
        seed = np.random.randint(2 ** 16 - 1)
    if distance is not None:
        raise NotImplementedError("distance argument not currently implemented")
    np.random.seed(seed)

    index = data.index if isinstance(data, pd.DataFrame) else None

    data = utils.toarray(data)
    if data.shape[1] > 3:
        warnings.warn(
            "Expected data to be low-dimensional. "
            "Got data.shape[1] = {}".format(data.shape[1]),
            UserWarning,
        )
    cluster_labels = utils.toarray(cluster_labels).flatten()
    if not cluster_labels.shape[0] == data.shape[0]:
        raise ValueError(
            "Expected len(cluster_labels) ({}) to equal "
            "data.shape[0] ({})".format(cluster_labels.shape[0], data.shape[0])
        )

    if start_cluster is not None:
        kwargs["start_cluster"] = start_cluster
    if end_cluster is not None:
        kwargs["end_cluster"] = end_cluster
    if omega is not None:
        kwargs["omega"] = omega

    slingshot = _Slingshot(
        data=data,
        cluster_labels=cluster_labels,
        shrink=shrink,
        extend=extend,
        reweight=reweight,
        reassign=reassign,
        thresh=thresh,
        max_iter=max_iter,
        stretch=stretch,
        smoother=smoother,
        shrink_method=shrink_method,
        allow_breaks=allow_breaks,
        **kwargs,
        seed=seed,
        rpy_verbose=verbose,
    )
    slingshot["curves"] = np.array(list(slingshot["curves"].values()))

    membership = (~np.isnan(slingshot["pseudotime"])).astype(int)
    branch = np.sum(membership * (2 ** np.arange(membership.shape[1])), axis=1)
    # reorder based on pseudotime
    branch_ids = np.unique(branch)
    branch_means = [
        np.nanmean(slingshot["pseudotime"][branch == id])
        if not np.all(np.isnan(slingshot["pseudotime"][branch == id]))
        else np.nan
        for id in branch_ids
    ]
    branch_order = np.argsort(branch_means)
    branch_old = branch.copy()
    for i in range(len(branch_order)):
        j = branch_order[i]
        if np.isnan(branch_means[j]):
            branch[branch_old == branch_ids[j]] = -1
        else:
            branch[branch_old == branch_ids[j]] = i
    slingshot["branch"] = branch

    if index is not None:
        slingshot["pseudotime"] = pd.DataFrame(slingshot["pseudotime"], index=index)
        slingshot["branch"] = pd.Series(slingshot["branch"], name="branch", index=index)
    return slingshot

Install the required R packages to run Slingshot.      Parameters     ----------     site_repository : string, optional (default: None)         additional repository in which to look for packages to install.         This repository will be prepended to the default repositories     update : boolean, optional (default: False)         When False, don't attempt to update old packages.         When True, update old packages automatically.     version : string, optional (default: None)         Bioconductor version to install, e.g., version = "3.8".         The special symbol version = "devel" installs the current 'development' version.         If None, installs from the current version.     verbose : boolean, optional (default: True)         Install script verbosity. library(slingshot) data, cluster_labels,         start_cluster = NULL, end_cluster = NULL,         distance = NULL, omega = NULL, lineages = list(), shrink = TRUE,         extend = "y", reweight = TRUE, reassign = TRUE, thresh = 0.001,         max_iter = 15, stretch = 2,         smoother = "smooth.spline", shrink_method = "cosine",         allow_breaks = TRUE, seed = NULL, ... set.seed(seed)         data <- as.matrix(data)         cluster_labels <- as.factor(cluster_labels)          # Run Slingshot         sling <- slingshot(data, clusterLabels = cluster_labels,                          start.clus = start_cluster, end.clus = end_cluster,                          dist.fun = distance, omega = omega, lineages = lineages,                          shrink = shrink, extend = extend, reweight = reweight,                          reassign = reassign, thresh = thresh,                          maxit = max_iter, stretch = stretch,                          smoother = smoother, shrink.method = shrink_method,                          allow.breaks = allow_breaks, ...)         list(pseudotime = slingPseudotime(sling),              curves = lapply(sling@curves, function(curve) curve$s[curve$ord,])) Perform lineage inference with Slingshot.      Given a reduced-dimensional data matrix n by p and a vector of cluster labels     (or matrix of soft cluster assignments, potentially including a -1 label for     "unclustered"), this function performs lineage inference using a cluster-based     minimum spanning tree and constructing simulatenous principal curves for branching     paths through the tree.      For more details, read about Slingshot on GitHub_ and Bioconductor_.      .. _GitHub: https://github.com/kstreet13/slingshot     .. _Bioconductor: https://bioconductor.org/packages/release/bioc/html/slingshot.html      Parameters     ----------     data : array-like, shape=[n_samples, n_dimensions]         matrix of (reduced dimension) coordinates         to be used for lineage inference.     cluster_labels : list-like, shape=[n_samples]         a vector of cluster labels, optionally including -1's for "unclustered."     start_cluster : string, optional (default: None)         indicates the cluster(s) of origin.         Lineages will be represented by paths coming out of this cluster.     end_cluster : string, optional (default: None)         indicates the cluster(s) which will be forced leaf nodes.         This introduces a constraint on the MST algorithm.     distance : callable, optional (default: None)         method for calculating distances between clusters.         Must take two matrices as input, corresponding to subsets of reduced_dim.         If the minimum cluster size is larger than the number dimensions,         the default is to use the joint covariance matrix to find squared distance         between cluster centers. If not, the default is to use the diagonal of the         joint covariance matrix. Not currently implemented     omega : float, optional (default: None)         this granularity parameter determines the distance between every         real cluster and the artificial cluster.         It is parameterized such that this distance is omega / 2,         making omega the maximum distance between two connected clusters.         By default, omega = Inf.     shrink : boolean or float, optional (default: True)         boolean or numeric between 0 and 1, determines whether and how much to shrink         branching lineages toward their average prior to the split.     extend : {'y', 'n', 'pc1'}, optional (default: "y")         how to handle root and leaf clusters of lineages when         constructing the initial, piece-wise linear curve.     reweight : boolean, optional (default: True)         whether to allow cells shared between lineages to be reweighted during         curve-fitting. If True, cells shared between lineages will be iteratively         reweighted based on the quantiles of their projection distances to each curve.     reassign : boolean, optional (default: True)         whether to reassign cells to lineages at each iteration.         If True, cells will be added to a lineage when their         projection distance to the curve is less than the median         distance for all cells currently assigned to the lineage.         Additionally, shared cells will be removed from a lineage if         their projection distance to the curve is above the 90th         percentile and their weight along the curve is less than 0.1.     thresh : float, optional (default: 0.001)         determines the convergence criterion. Percent change in the         total distance from cells to their projections along curves         must be less than thresh.     max_iter : int, optional (default: 15)         maximum number of iterations     stretch : int, optional (default: 2)         factor between 0 and 2 by which curves can be extrapolated beyond endpoints     smoother : {"smooth.spline", "lowess", "periodic_lowess"},         optional (default: "smooth.spline")         choice of smoother. "periodic_lowess" allows one to fit closed         curves. Beware, you may want to use iter = 0 with "lowess".     shrink_method : string, optional (default: "cosine")         how to determine the appropriate amount of shrinkage for a         branching lineage. Accepted values: "gaussian", "rectangular",         "triangular", "epanechnikov", "biweight", "triweight",         "cosine", "optcosine", "density".     allow_breaks : boolean, optional (default: True)         determines whether curves that branch very close to the origin         should be allowed to have different starting points.     seed : int or None, optional (default: None)         Seed to use for generating random numbers.     verbose : int, optional (default: 1)         Logging verbosity between 0 and 2.      Returns     -------     slingshot : dict         Contains the following keys:     pseudotime : array-like, shape=[n_samples, n_curves]         Pseudotime projection of each cell onto each principal curve.         Value is `np.nan` if the cell does not lie on the curve     branch : list-like, shape=[n_samples]         Branch assignment for each cell     curves : array_like, shape=[n_curves, n_samples, n_dimensions]         Coordinates of each principle curve in the reduced dimension      Examples     --------     >>> import scprep     >>> import phate     >>> data, clusters = phate.tree.gen_dla(n_branch=4, n_dim=200, branch_length=200)     >>> phate_op = phate.PHATE()     >>> data_phate = phate_op.fit_transform(data)     >>> slingshot = scprep.run.Slingshot(data_phate, clusters)     >>> ax = scprep.plot.scatter2d(     ...     data_phate,     ...     c=slingshot['pseudotime'][:,0],     ...     cmap='magma',     ...     legend_title='Branch 1'     ... )     >>> scprep.plot.scatter2d(     ...     data_phate,     ...     c=slingshot['pseudotime'][:,1],     ...     cmap='viridis',     ...     ax=ax,     ...     ticks=False,     ...     label_prefix='PHATE',     ...     legend_title='Branch 2'     ...     )     >>> for curve in slingshot['curves']:     ...     ax.plot(curve[:,0], curve[:,1], c='black')     >>> ax = scprep.plot.scatter2d(data_phate, c=slingshot['branch'],     ...                        legend_title='Branch', ticks=False, label_prefix='PHATE')     >>> for curve in slingshot['curves']:     ...     ax.plot(curve[:,0], curve[:,1], c='black') # reorder based on pseudotime