Skip to content

Commit dbcc349

Browse files
authored
Merge pull request #178 from jpintar/use_numpy_random_generator
Switch to using numpy.random.Generator throughout
2 parents 807aba2 + 7bf73c9 commit dbcc349

34 files changed

Lines changed: 563 additions & 266 deletions

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ ____
7373
Release Notes
7474
-------------
7575

76+
### Version 1.4.5 (future)
77+
* Use `numpy.random.Generator` in place of the legacy global `numpy.random.RandomState` and require
78+
`numpy>=1.17`. Note that since the default PRNG is now [PCG64](https://numpy.org/devdocs/reference/random/bit_generators/pcg64.html) instead of the Mersenne Twister, numerical outputs are expected to differ from
79+
those in previous versions. If exact reproducibility is required, users should pin the relevant prior version.
80+
7681
### Version 1.4.4
7782
* Fix: reorder dependencies to work around uv resolver order-dependence ([astral-sh/uv#5161](https://github.com/astral-sh/uv/issues/5161)), which caused `uv pip install palantir` to resolve to incompatible ancient versions of scanpy/numba/llvmlite
7883

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ dependencies = [
3838
"igraph>=0.11.8",
3939
"scikit-learn",
4040
"matplotlib>=3.8.0",
41-
"numpy>=1.14.2",
41+
"numpy>=1.17.0",
4242
"pandas>=0.22.0",
4343
"scipy>=1.3",
4444
"networkx>=2.1",

src/palantir/core.py

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
Core functions for running Palantir
33
"""
44

5-
from typing import Union, Optional, List, Dict, Tuple
5+
from __future__ import annotations
6+
67
import numpy as np
78
import pandas as pd
89
import networkx as nx
@@ -19,6 +20,7 @@
1920
from scipy.sparse.csgraph import connected_components
2021
from scipy.stats import entropy, pearsonr, norm
2122
from numpy.linalg import inv, pinv, LinAlgError
23+
from numpy.random import BitGenerator, SeedSequence, RandomState
2224
import warnings
2325
from anndata import AnnData
2426

@@ -59,9 +61,9 @@ def _get_joblib_backend():
5961

6062

6163
def run_palantir(
62-
data: Union[pd.DataFrame, AnnData],
63-
early_cell,
64-
terminal_states: Optional[Union[List, Dict, pd.Series]] = None,
64+
data: pd.DataFrame | AnnData,
65+
early_cell: str,
66+
terminal_states: list | dict | pd.Series | None = None,
6567
knn: int = 30,
6668
num_waypoints: int = 1200,
6769
n_jobs: int = -1,
@@ -72,21 +74,21 @@ def run_palantir(
7274
pseudo_time_key: str = "palantir_pseudotime",
7375
entropy_key: str = "palantir_entropy",
7476
fate_prob_key: str = "palantir_fate_probabilities",
75-
save_as_df: bool = None,
77+
save_as_df: bool | None = None,
7678
waypoints_key: str = "palantir_waypoints",
77-
seed: int = 20,
78-
) -> Optional[object]:
79+
seed: int | np.ndarray | np.random.Generator | BitGenerator | SeedSequence | RandomState | None = 20,
80+
) -> object | None:
7981
"""
8082
Executes the Palantir algorithm to derive pseudotemporal ordering of cells, their fate probabilities, and
8183
state entropy based on the multiscale diffusion map results.
8284
8385
Parameters
8486
----------
85-
data : Union[pd.DataFrame, AnnData]
87+
data : pd.DataFrame | AnnData
8688
Either a DataFrame of multiscale space diffusion components or a Scanpy AnnData object.
8789
early_cell : str
8890
Start cell for pseudotime construction.
89-
terminal_states : List/Series/Dict, optional
91+
terminal_states : list | pd.Series | dict, optional
9092
User-defined terminal states structure in the format {terminal_name:cell_name}. Default is None.
9193
knn : int, optional
9294
Number of nearest neighbors for graph construction. Default is 30.
@@ -116,12 +118,12 @@ def run_palantir(
116118
write h5ad files with DataFrames in ad.obsm. Default is palantir.SAVE_AS_DF = True.
117119
waypoints_key : str, optional
118120
Key to store the waypoints in uns of the AnnData object. Default is 'palantir_waypoints'.
119-
seed : int, optional
121+
seed : int | np.ndarray | np.random.Generator | BitGenerator | SeedSequence | RandomState | None, optional
120122
The seed for the random number generator used in waypoint sampling. Default is 20.
121123
122124
Returns
123125
-------
124-
Optional[PResults]
126+
PResults | None
125127
PResults object with pseudotime, entropy, branch probabilities, and waypoints.
126128
If an AnnData object is passed as data, the result is written to its obs, obsm, and uns attributes
127129
using the provided keys and None is returned.
@@ -250,7 +252,9 @@ def run_palantir(
250252

251253

252254
def _max_min_sampling(
253-
data: pd.DataFrame, num_waypoints: int, seed: Optional[int] = None
255+
data: pd.DataFrame,
256+
num_waypoints: int,
257+
seed: int | np.ndarray | np.random.Generator | BitGenerator | SeedSequence | RandomState | None = 20,
254258
) -> pd.Index:
255259
"""Function for max min sampling of waypoints.
256260
@@ -263,8 +267,8 @@ def _max_min_sampling(
263267
Data matrix along which to sample the waypoints, usually diffusion components.
264268
num_waypoints : int
265269
Number of waypoints to sample.
266-
seed : Optional[int], default=None
267-
Random number generator seed for the initial point selection.
270+
seed : int | np.ndarray | np.random.Generator | BitGenerator | SeedSequence | RandomState | None, optional
271+
Random number generator seed for the initial point selection. Default is 20
268272
269273
Returns
270274
-------
@@ -282,16 +286,15 @@ def _max_min_sampling(
282286
)
283287
num_waypoints = min_waypoints
284288
no_iterations = int((num_waypoints) / data.shape[1])
285-
if seed is not None:
286-
np.random.seed(seed)
289+
rng = np.random.default_rng(seed)
287290

288291
N = data.shape[0]
289292
data_values = data.values
290293

291294
for i, ind in enumerate(data.columns):
292295
vec = data_values[:, i]
293296

294-
current_wp = np.random.randint(N)
297+
current_wp = rng.integers(N)
295298
iter_set = [
296299
current_wp,
297300
]
@@ -319,7 +322,7 @@ def _compute_pseudotime(
319322
waypoints: pd.Index,
320323
n_jobs: int,
321324
max_iterations: int = 25,
322-
) -> Tuple[pd.Series, pd.DataFrame]:
325+
) -> tuple[pd.Series, pd.DataFrame]:
323326
"""Compute pseudotime and weight matrix using shortest path distances.
324327
325328
This function constructs a kNN graph and computes shortest path distances from
@@ -343,7 +346,7 @@ def _compute_pseudotime(
343346
344347
Returns
345348
-------
346-
Tuple[pd.Series, pd.DataFrame]
349+
tuple[pd.Series, pd.DataFrame]
347350
pseudotime : pd.Series
348351
Pseudotime ordering of cells.
349352
W : pd.DataFrame
@@ -408,8 +411,8 @@ def identify_terminal_states(
408411
num_waypoints: int = 1200,
409412
n_jobs: int = -1,
410413
max_iterations: int = 25,
411-
seed: int = 20,
412-
) -> Tuple[np.ndarray, pd.Index]:
414+
seed: int | np.ndarray | np.random.Generator | BitGenerator | SeedSequence | RandomState | None = 20,
415+
) -> tuple[np.ndarray, pd.Index]:
413416
"""
414417
Identify terminal states from multi-scale data.
415418
@@ -430,12 +433,12 @@ def identify_terminal_states(
430433
Number of jobs for parallel processing. Default is -1.
431434
max_iterations : int, optional
432435
Maximum number of iterations for pseudotime convergence. Default is 25.
433-
seed : int, optional
436+
seed : int | np.ndarray | np.random.Generator | BitGenerator | SeedSequence | RandomState | None, optional
434437
Random seed for waypoint sampling. Default is 20.
435438
436439
Returns
437440
-------
438-
Tuple[np.ndarray, pd.Index]
441+
tuple[np.ndarray, pd.Index]
439442
terminal_states : np.ndarray
440443
Array of identified terminal state cells.
441444
excluded_boundaries : pd.Index
@@ -650,12 +653,12 @@ def _terminal_states_from_markov_chain(
650653

651654

652655
def _differentiation_entropy(
653-
wp_data: pd.DataFrame,
654-
terminal_states: Optional[np.ndarray],
655-
knn: int,
656-
n_jobs: int,
657-
pseudotime: pd.Series
658-
) -> Tuple[pd.Series, pd.DataFrame]:
656+
wp_data: pd.DataFrame,
657+
terminal_states: np.ndarray | None,
658+
knn: int,
659+
n_jobs: int,
660+
pseudotime: pd.Series,
661+
) -> tuple[pd.Series, pd.DataFrame]:
659662
"""Compute entropy and branch probabilities from a Markov chain.
660663
661664
This function constructs a Markov chain from waypoints data and computes
@@ -665,7 +668,7 @@ def _differentiation_entropy(
665668
----------
666669
wp_data : pd.DataFrame
667670
Multi-scale data of the waypoints.
668-
terminal_states : Optional[np.ndarray]
671+
terminal_states : np.ndarray, optional
669672
Terminal states to use for probability calculations. If None, they will be
670673
automatically detected.
671674
knn : int
@@ -677,7 +680,7 @@ def _differentiation_entropy(
677680
678681
Returns
679682
-------
680-
Tuple[pd.Series, pd.DataFrame]
683+
tuple[pd.Series, pd.DataFrame]
681684
entropy : pd.Series
682685
Differentiation entropy for each cell.
683686
branch_probs : pd.DataFrame

src/palantir/plot_utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,9 @@ def color_generator(exclude):
281281
yield clr
282282
hex_digits = np.array(list("0123456789ABCDEF"))
283283
# If default cycle is exhausted, generate random colors.
284+
rng = np.random.default_rng()
284285
while True:
285-
new_color = "#" + "".join(np.random.choice(hex_digits, size=6))
286+
new_color = "#" + "".join(rng.choice(hex_digits, size=6))
286287
if new_color not in exclude:
287288
yield new_color
288289

0 commit comments

Comments
 (0)