Exemple #1
0
    def __init__(
        self,
        obj: Union[AnnData, np.ndarray, spmatrix, KernelExpression],
        key: Optional[str] = None,
        obsp_key: Optional[str] = None,
        write_to_adata: bool = True,
    ):
        if isinstance(obj, KernelExpression):
            self._kernel = obj
        elif isinstance(obj, (np.ndarray, spmatrix)):
            self._kernel = PrecomputedKernel(obj)
        elif isinstance(obj, AnnData):
            if obsp_key is None:
                raise ValueError(
                    "Please specify `obsp_key=...` when supplying an AnnData object."
                )
            elif obsp_key not in obj.obsp.keys():
                raise KeyError(f"Key `{obsp_key!r}` not found in `adata.obsp`.")
            self._kernel = PrecomputedKernel(obj.obsp[obsp_key], adata=obj)
        else:
            raise TypeError(
                f"Expected an object of type `KernelExpression`, `numpy.ndarray`, `scipy.sparse.spmatrix` "
                f"or `anndata.AnnData`, got `{type(obj).__name__!r}`."
            )

        if self.kernel.transition_matrix is None:
            logg.debug("Computing transition matrix using default parameters")
            self.kernel.compute_transition_matrix()

        if write_to_adata:
            self.kernel.write_to_adata(key=key)
Exemple #2
0
    def __init__(
        self,
        obj: Union[AnnData, np.ndarray, spmatrix, KernelExpression],
        key: Optional[str] = None,
        obsp_key: Optional[str] = None,
        write_to_adata: bool = True,
    ):
        if isinstance(obj, KernelExpression):
            self._kernel = obj
        elif isinstance(obj, (np.ndarray, spmatrix)):
            self._kernel = PrecomputedKernel(obj)
        elif isinstance(obj, AnnData):
            if obsp_key is None:
                raise ValueError(
                    "Specify `obsp_key=...` when supplying an `AnnData` object."
                )
            elif obsp_key not in obj.obsp.keys():
                raise KeyError(
                    f"Key `{obsp_key!r}` not found in `adata.obsp`.")
            self._kernel = PrecomputedKernel(obj.obsp[obsp_key], adata=obj)
        else:
            raise TypeError(
                f"Expected an object of type `KernelExpression`, `numpy.ndarray`, `scipy.sparse.spmatrix` "
                f"or `anndata.AnnData`, got `{type(obj).__name__!r}`.")

        if self.kernel._transition_matrix is None:
            # access the private attribute to avoid accidentally computing the transition matrix
            # in principle, it doesn't make a difference, apart from not seeing the message
            logg.warning(
                "Computing transition matrix using the default parameters")
            self.kernel.compute_transition_matrix()

        if write_to_adata:
            self.kernel.write_to_adata(key=key)
Exemple #3
0
    def test_precomputed_no_adata(self):
        pk = PrecomputedKernel(random_transition_matrix(50))
        pk.write_to_adata()

        assert isinstance(pk.adata, AnnData)
        assert pk.adata.shape == (50, 1)
        assert pk.adata.obs.shape == (50, 0)
        assert pk.adata.var.shape == (1, 0)
        assert "T_fwd_params" in pk.adata.uns.keys()
        np.testing.assert_array_equal(pk.adata.obsp["T_fwd"].toarray(),
                                      pk.transition_matrix.toarray())
Exemple #4
0
    def test_precomputed_from_kernel(self, adata: AnnData):
        vk = VelocityKernel(adata).compute_transition_matrix(
            mode="stochastic",
            softmax_scale=4,
        )

        pk = PrecomputedKernel(vk)
        pk.write_to_adata()

        assert pk.adata is vk.adata
        assert pk._origin == str(vk).strip("~<>")
        assert pk.params is not vk.params
        assert pk.params == vk.params
        assert pk.transition_matrix is not vk.transition_matrix
        np.testing.assert_array_equal(pk.transition_matrix.A,
                                      vk.transition_matrix.A)
Exemple #5
0
    def test_precomputed_adata_origin(self, adata: AnnData):
        vk = VelocityKernel(adata).compute_transition_matrix(mode="stochastic",
                                                             softmax_scale=4)
        vk.write_to_adata("foo")

        pk = PrecomputedKernel("foo", adata=adata)

        assert pk._origin == "adata.obsp['foo']"
Exemple #6
0
    def test_precomputed_sum(self, adata: AnnData):
        mat = random_transition_matrix(adata.n_obs)
        pk = PrecomputedKernel(mat)
        vk = VelocityKernel(adata).compute_transition_matrix(softmax_scale=4)

        expected = (0.5 * vk.transition_matrix) + (0.5 * pk.transition_matrix)
        actual = (pk + vk).compute_transition_matrix()

        np.testing.assert_array_almost_equal(
            expected.toarray(), actual.transition_matrix.toarray())
Exemple #7
0
    def test_precomputed_different_adata(self, adata: AnnData):
        vk = VelocityKernel(adata).compute_transition_matrix(mode="stochastic",
                                                             softmax_scale=4)
        bdata = adata.copy()

        pk = PrecomputedKernel(vk, adata=bdata)

        assert pk.adata is adata
        assert pk.adata is vk.adata
        assert pk.adata is not bdata
Exemple #8
0
    def write(self,
              fname: Union[str, Path],
              ext: Optional[str] = "pickle") -> None:
        """
        %(pickleable.full_desc)s

        Parameters
        ----------
        %(pickleable.parameters)s

        Returns
        -------
        %(pickleable.returns)s
        """  # noqa

        fname = str(fname)
        if ext is not None:
            if not ext.startswith("."):
                ext = "." + ext
            if not fname.endswith(ext):
                fname += ext

        logg.debug(f"Writing to `{fname}`")

        with open(fname, "wb") as fout:
            if version_info[:2] > (3, 6):
                pickle.dump(self, fout)
            else:
                # we need to use PrecomputedKernel because Python3.6 can't pickle Enums
                # and they are present in VelocityKernel
                logg.warning(
                    "Saving kernel as `cellrank.tl.kernels.PrecomputedKernel`")
                orig_kernel = self.kernel
                self._kernel = PrecomputedKernel(self.kernel)
                try:
                    pickle.dump(self, fout)
                except Exception as e:
                    raise e
                finally:
                    self._kernel = orig_kernel
Exemple #9
0
    def test_precomputed_transition_matrix(self, adata: AnnData):
        mat = random_transition_matrix(adata.n_obs)
        pk = PrecomputedKernel(mat)

        np.testing.assert_array_equal(mat, pk.transition_matrix.toarray())
Exemple #10
0
    def test_precomputed_adata(self, adata: AnnData):
        pk = PrecomputedKernel(random_transition_matrix(adata.n_obs),
                               adata=adata)

        assert pk.adata is adata
Exemple #11
0
 def test_precomputed_not_a_transition_matrix(self):
     mat = random_transition_matrix(100)
     mat[0, 0] = 0xDEADBEEF
     with pytest.raises(ValueError):
         _ = PrecomputedKernel(mat)
Exemple #12
0
 def test_precomputed_not_square(self):
     with pytest.raises(ValueError):
         _ = PrecomputedKernel(np.random.normal(size=(10, 9)))
Exemple #13
0
 def test_precomputed_not_array(self):
     with pytest.raises(TypeError):
         _ = PrecomputedKernel([[1, 0], [0, 1]])
def _initial_terminal(
    adata: AnnData,
    estimator: type(BaseEstimator) = GPCCA,
    backward: bool = False,
    mode: str = VelocityMode.DETERMINISTIC.s,
    backward_mode: str = BackwardMode.TRANSPOSE.s,
    n_states: Optional[int] = None,
    cluster_key: Optional[str] = None,
    key: Optional[str] = None,
    show_plots: bool = False,
    copy: bool = False,
    return_estimator: bool = False,
    fit_kwargs: Mapping = MappingProxyType({}),
    **kwargs,
) -> Optional[Union[AnnData, BaseEstimator]]:

    _check_estimator_type(estimator)

    try:
        kernel = PrecomputedKernel(key, adata=adata, backward=backward)
        write_to_adata = False  # no need to write
        logg.info("Using precomputed transition matrix")
    except KeyError:
        # compute kernel object
        kernel = transition_matrix(
            adata,
            backward=backward,
            mode=mode,
            backward_mode=backward_mode,
            **kwargs,
        )
        write_to_adata = True

    # create estimator object
    mc = estimator(
        kernel,
        read_from_adata=False,
        inplace=not copy,
        key=key,
        write_to_adata=write_to_adata,
    )

    if cluster_key is None:
        _info_if_obs_keys_categorical_present(
            adata,
            keys=["louvain", "leiden", "clusters"],
            msg_fmt=
            "Found categorical observation in `adata.obs[{!r}]`. Consider specifying it as `cluster_key`.",
        )

    mc.fit(
        n_lineages=n_states,
        cluster_key=cluster_key,
        compute_absorption_probabilities=False,
        **fit_kwargs,
    )

    if show_plots:
        mc.plot_spectrum(real_only=True)
        if isinstance(mc, CFLARE):
            mc.plot_eigendecomposition(abs_value=True,
                                       perc=[0, 98],
                                       use=n_states)
            mc.plot_terminal_states(discrete=True, same_plot=False)
        elif isinstance(mc, GPCCA):
            n_states = len(mc._get(P.MACRO).cat.categories)
            if n_states > 1:
                mc.plot_schur()
            mc.plot_terminal_states(discrete=True, same_plot=False)
            if n_states > 1:
                mc.plot_coarse_T()
        else:
            raise NotImplementedError(
                f"Pipeline not implemented for `{type(mc).__name__!r}.`")

    return mc.adata if copy else mc if return_estimator else None
Exemple #15
0
def lineages(
    adata: AnnData,
    backward: bool = False,
    copy: bool = False,
    return_estimator: bool = False,
    **kwargs,
) -> Optional[AnnData]:
    """
    Compute probabilistic lineage assignment using RNA velocity.

    For each cell `i` in :math:`{1, ..., N}` and %(initial_or_terminal)s state `j` in :math:`{1, ..., M}`,
    the probability is computed that cell `i` is either going to %(terminal)s state `j` (``backward=False``)
    or is coming from %(initial)s state `j` (``backward=True``).

    This function computes the absorption probabilities of a Markov chain towards the %(initial_or_terminal) states
    uncovered by :func:`cellrank.tl.initial_states` or :func:`cellrank.tl.terminal_states` using a highly efficient
    implementation that scales to large cell numbers.

    It's also possible to calculate mean and variance of the time until absorption for all or just a subset
    of the %(initial_or_terminal)s states. This can be seen as a pseudotemporal measure, either towards any terminal
    population of the state change trajectory, or towards specific ones.

    Parameters
    ----------
    %(adata)s
    %(backward)s
    copy
        Whether to update the existing ``adata`` object or to return a copy.
    return_estimator
        Whether to return the estimator. Only available when ``copy=False``.
    **kwargs
        Keyword arguments for :meth:`cellrank.tl.estimators.BaseEstimator.compute_absorption_probabilities`.

    Returns
    -------
    :class:`anndata.AnnData`, :class:`cellrank.tl.estimators.BaseEstimator` or :obj:`None`
        Depending on ``copy`` and ``return_estimator``, either updates the existing ``adata`` object,
        returns its copy or returns the estimator.
    """

    if backward:
        lin_key = AbsProbKey.BACKWARD
        fs_key = TermStatesKey.BACKWARD
        fs_key_pretty = TerminalStatesPlot.BACKWARD
    else:
        lin_key = AbsProbKey.FORWARD
        fs_key = TermStatesKey.FORWARD
        fs_key_pretty = TerminalStatesPlot.FORWARD

    try:
        pk = PrecomputedKernel(adata=adata, backward=backward)
    except KeyError as e:
        raise RuntimeError(
            f"Compute transition matrix first as `cellrank.tl.transition_matrix(..., backward={backward})`."
        ) from e

    start = logg.info(
        f"Computing lineage probabilities towards {fs_key_pretty.s}")
    mc = GPCCA(
        pk, read_from_adata=True, inplace=not copy
    )  # GPCCA is more general than CFLARE, in terms of what is saves
    if mc._get(P.TERM) is None:
        raise RuntimeError(
            f"Compute the states first as `cellrank.tl.{fs_key.s}(..., backward={backward})`."
        )

    # compute the absorption probabilities
    mc.compute_absorption_probabilities(**kwargs)

    logg.info(f"Adding lineages to `adata.obsm[{lin_key.s!r}]`\n    Finish",
              time=start)

    return mc.adata if copy else mc if return_estimator else None
Exemple #16
0
    def test_precomputed_from_kernel_no_transition(self, adata: AnnData):
        vk = VelocityKernel(adata)

        with pytest.raises(ValueError):
            PrecomputedKernel(vk)